001/*
002 * Copyright (C) 2009-2011 Mathias Doenitz
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.parboiled.support;
018
019import static org.parboiled.common.Preconditions.checkArgNotNull;
020import static org.parboiled.common.Preconditions.checkArgument;
021
022/**
023 * A simple immutable container for a range of indices into an underlying InputBuffer.
024 */
025public final class IndexRange {
026
027    public static final IndexRange EMPTY = new IndexRange(0, 0);
028    
029    /**
030     * The index of the first character in the range.
031     */
032    public final int start;
033
034    /**
035     * The index of the character following the last character of the range.
036     */
037    public final int end;
038
039    public IndexRange(int start, int end) {
040        checkArgument(start >= 0, "start must be >= 0");
041        checkArgument(end >= start, "end must be >= start");
042        this.start = start;
043        this.end = end;
044    }
045
046    /**
047     * Determines whether this range contains no characters.
048     *
049     * @return true if the end matches the start of the range.
050     */
051    public boolean isEmpty() {
052        return start == end;
053    }
054
055    /**
056     * @return the number of characters covered by this range
057     */
058    public int length() {
059        return end - start;
060    }
061
062    /**
063     * Determines whether this range overlaps with the given other one.
064     *
065     * @param other the other range
066     * @return true if there is at least one index that is contained in both ranges
067     */
068    public boolean overlapsWith(IndexRange other) {
069        checkArgNotNull(other, "other");
070        return end > other.start && other.end > start;
071    }
072
073    /**
074     * Determines whether this range immediated follows the given other one.
075     *
076     * @param other the other range
077     * @return true if this range immediated follows the given other one
078     */
079    public boolean isPrecededBy(IndexRange other) {
080        checkArgNotNull(other, "other");
081        return other.end == start;
082    }
083
084    /**
085     * Determines whether this range is immediated followed by the given other one.
086     *
087     * @param other the other range
088     * @return true if this range is immediated followed by the given other one
089     */
090    public boolean isFollowedBy(IndexRange other) {
091        checkArgNotNull(other, "other");
092        return end == other.start;
093    }
094
095    /**
096     * Determines whether this range immediated follows or precedes the given other one.
097     *
098     * @param other the other range
099     * @return true if this range immediated follows or precedes the given other one.
100     */
101    public boolean touches(IndexRange other) {
102        checkArgNotNull(other, "other");
103        return other.end == start || end == other.start;
104    }
105
106    /**
107     * Created a new IndexRange that spans all characters between the smallest and the highest index of the two ranges.
108     *
109     * @param other the other range
110     * @return a new IndexRange instance
111     */
112    public IndexRange mergedWith(IndexRange other) {
113        checkArgNotNull(other, "other");
114        return new IndexRange(Math.min(start, other.start), Math.max(end, other.end));
115    }
116
117    @Override
118    public boolean equals(Object o) {
119        if (this == o) return true;
120        if (!(o instanceof IndexRange)) return false;
121        IndexRange that = (IndexRange) o;
122        return end == that.end && start == that.start;
123    }
124
125    @Override
126    public int hashCode() {
127        int result = start;
128        result = 31 * result + end;
129        return result;
130    }
131
132    @Override
133    public String toString() {
134        return "IndexRange{" +
135                "start=" + start +
136                ", end=" + end +
137                '}';
138    }
139}