All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.antlr.v4.runtime.misc.IntervalSet Maven / Gradle / Ivy

There is a newer version: 4.9.0
Show newest version
/*
 * Copyright (c) 2012 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD-3-Clause license that
 * can be found in the LICENSE.txt file in the project root.
 */
package org.antlr.v4.runtime.misc;

import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.VocabularyImpl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

/**
 * This class implements the {@link IntSet} backed by a sorted array of
 * non-overlapping intervals. It is particularly efficient for representing
 * large collections of numbers, where the majority of elements appear as part
 * of a sequential range of numbers that are all part of the set. For example,
 * the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }.
 *
 * 

* This class is able to represent sets containing any combination of values in * the range {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE} * (inclusive).

*/ public class IntervalSet implements IntSet { public static final IntervalSet COMPLETE_CHAR_SET = IntervalSet.of(Lexer.MIN_CHAR_VALUE, Lexer.MAX_CHAR_VALUE); static { COMPLETE_CHAR_SET.setReadonly(true); } public static final IntervalSet EMPTY_SET = new IntervalSet(); static { EMPTY_SET.setReadonly(true); } /** The list of sorted, disjoint intervals. */ protected List intervals; protected boolean readonly; public IntervalSet(List intervals) { this.intervals = intervals; } public IntervalSet(IntervalSet set) { this(); addAll(set); } public IntervalSet(int... els) { if ( els==null ) { intervals = new ArrayList(2); // most sets are 1 or 2 elements } else { intervals = new ArrayList(els.length); for (int e : els) add(e); } } /** Create a set with a single element, el. */ @NotNull public static IntervalSet of(int a) { IntervalSet s = new IntervalSet(); s.add(a); return s; } /** Create a set with all ints within range [a..b] (inclusive) */ public static IntervalSet of(int a, int b) { IntervalSet s = new IntervalSet(); s.add(a,b); return s; } public void clear() { if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet"); intervals.clear(); } /** Add a single element to the set. An isolated element is stored * as a range el..el. */ @Override public void add(int el) { if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet"); add(el,el); } /** Add interval; i.e., add all integers from a to b to set. * If b<a, do nothing. * Keep list in sorted order (by left range value). * If overlap, combine ranges. For example, * If this is {1..5, 10..20}, adding 6..7 yields * {1..5, 6..7, 10..20}. Adding 4..8 yields {1..8, 10..20}. */ public void add(int a, int b) { add(Interval.of(a, b)); } // copy on write so we can cache a..a intervals and sets of that protected void add(Interval addition) { if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet"); //System.out.println("add "+addition+" to "+intervals.toString()); if ( addition.b iter = intervals.listIterator(); iter.hasNext();) { Interval r = iter.next(); if ( addition.equals(r) ) { return; } if ( addition.adjacent(r) || !addition.disjoint(r) ) { // next to each other, make a single larger interval Interval bigger = addition.union(r); iter.set(bigger); // make sure we didn't just create an interval that // should be merged with next interval in list while ( iter.hasNext() ) { Interval next = iter.next(); if ( !bigger.adjacent(next) && bigger.disjoint(next) ) { break; } // if we bump up against or overlap next, merge iter.remove(); // remove this one iter.previous(); // move backwards to what we just set iter.set(bigger.union(next)); // set to 3 merged ones iter.next(); // first call to next after previous duplicates the result } return; } if ( addition.startsBeforeDisjoint(r) ) { // insert before r iter.previous(); iter.add(addition); return; } // if disjoint and after r, a future iteration will handle it } // ok, must be after last interval (and disjoint from last interval) // just add it intervals.add(addition); } /** combine all sets in the array returned the or'd value */ public static IntervalSet or(IntervalSet[] sets) { IntervalSet r = new IntervalSet(); for (IntervalSet s : sets) r.addAll(s); return r; } @Override public IntervalSet addAll(IntSet set) { if ( set==null ) { return this; } if (set instanceof IntervalSet) { IntervalSet other = (IntervalSet)set; // walk set and add each interval int n = other.intervals.size(); for (int i = 0; i < n; i++) { Interval I = other.intervals.get(i); this.add(I.a,I.b); } } else { for (int value : set.toList()) { add(value); } } return this; } public IntervalSet complement(int minElement, int maxElement) { return this.complement(IntervalSet.of(minElement,maxElement)); } /** {@inheritDoc} */ @Override public IntervalSet complement(IntSet vocabulary) { if ( vocabulary==null || vocabulary.isNil() ) { return null; // nothing in common with null set } IntervalSet vocabularyIS; if (vocabulary instanceof IntervalSet) { vocabularyIS = (IntervalSet)vocabulary; } else { vocabularyIS = new IntervalSet(); vocabularyIS.addAll(vocabulary); } return vocabularyIS.subtract(this); } @Override public IntervalSet subtract(IntSet a) { if (a == null || a.isNil()) { return new IntervalSet(this); } if (a instanceof IntervalSet) { return subtract(this, (IntervalSet)a); } IntervalSet other = new IntervalSet(); other.addAll(a); return subtract(this, other); } /** * Compute the set difference between two interval sets. The specific * operation is {@code left - right}. If either of the input sets is * {@code null}, it is treated as though it was an empty set. */ @NotNull public static IntervalSet subtract(@Nullable IntervalSet left, @Nullable IntervalSet right) { if (left == null || left.isNil()) { return new IntervalSet(); } IntervalSet result = new IntervalSet(left); if (right == null || right.isNil()) { // right set has no elements; just return the copy of the current set return result; } int resultI = 0; int rightI = 0; while (resultI < result.intervals.size() && rightI < right.intervals.size()) { Interval resultInterval = result.intervals.get(resultI); Interval rightInterval = right.intervals.get(rightI); // operation: (resultInterval - rightInterval) and update indexes if (rightInterval.b < resultInterval.a) { rightI++; continue; } if (rightInterval.a > resultInterval.b) { resultI++; continue; } Interval beforeCurrent = null; Interval afterCurrent = null; if (rightInterval.a > resultInterval.a) { beforeCurrent = new Interval(resultInterval.a, rightInterval.a - 1); } if (rightInterval.b < resultInterval.b) { afterCurrent = new Interval(rightInterval.b + 1, resultInterval.b); } if (beforeCurrent != null) { if (afterCurrent != null) { // split the current interval into two result.intervals.set(resultI, beforeCurrent); result.intervals.add(resultI + 1, afterCurrent); resultI++; rightI++; continue; } else { // replace the current interval result.intervals.set(resultI, beforeCurrent); resultI++; continue; } } else { if (afterCurrent != null) { // replace the current interval result.intervals.set(resultI, afterCurrent); rightI++; continue; } else { // remove the current interval (thus no need to increment resultI) result.intervals.remove(resultI); continue; } } } // If rightI reached right.intervals.size(), no more intervals to subtract from result. // If resultI reached result.intervals.size(), we would be subtracting from an empty set. // Either way, we are done. return result; } @Override public IntervalSet or(IntSet a) { IntervalSet o = new IntervalSet(); o.addAll(this); o.addAll(a); return o; } /** {@inheritDoc} */ @Override public IntervalSet and(IntSet other) { if ( other==null ) { //|| !(other instanceof IntervalSet) ) { return null; // nothing in common with null set } List myIntervals = this.intervals; List theirIntervals = ((IntervalSet)other).intervals; IntervalSet intersection = null; int mySize = myIntervals.size(); int theirSize = theirIntervals.size(); int i = 0; int j = 0; // iterate down both interval lists looking for nondisjoint intervals while ( i1 ) { buf.append("{"); } Iterator iter = this.intervals.iterator(); while (iter.hasNext()) { Interval I = iter.next(); int a = I.a; int b = I.b; if ( a==b ) { if ( a==Token.EOF ) buf.append(""); else if ( elemAreChar ) buf.append("'").appendCodePoint(a).append("'"); else buf.append(a); } else { if ( elemAreChar ) buf.append("'").appendCodePoint(a).append("'..'").appendCodePoint(b).append("'"); else buf.append(a).append("..").append(b); } if ( iter.hasNext() ) { buf.append(", "); } } if ( this.size()>1 ) { buf.append("}"); } return buf.toString(); } /** * @deprecated Use {@link #toString(Vocabulary)} instead. */ @Deprecated public String toString(String[] tokenNames) { return toString(VocabularyImpl.fromTokenNames(tokenNames)); } public String toString(@NotNull Vocabulary vocabulary) { StringBuilder buf = new StringBuilder(); if ( this.intervals==null || this.intervals.isEmpty() ) { return "{}"; } if ( this.size()>1 ) { buf.append("{"); } Iterator iter = this.intervals.iterator(); while (iter.hasNext()) { Interval I = iter.next(); int a = I.a; int b = I.b; if ( a==b ) { buf.append(elementName(vocabulary, a)); } else { for (int i=a; i<=b; i++) { if ( i>a ) buf.append(", "); buf.append(elementName(vocabulary, i)); } } if ( iter.hasNext() ) { buf.append(", "); } } if ( this.size()>1 ) { buf.append("}"); } return buf.toString(); } /** * @deprecated Use {@link #elementName(Vocabulary, int)} instead. */ @Deprecated protected String elementName(String[] tokenNames, int a) { return elementName(VocabularyImpl.fromTokenNames(tokenNames), a); } @NotNull protected String elementName(@NotNull Vocabulary vocabulary, int a) { if (a == Token.EOF) { return ""; } else if (a == Token.EPSILON) { return ""; } else { return vocabulary.getDisplayName(a); } } @Override public int size() { int n = 0; int numIntervals = intervals.size(); if ( numIntervals==1 ) { Interval firstInterval = this.intervals.get(0); return firstInterval.b-firstInterval.a+1; } for (int i = 0; i < numIntervals; i++) { Interval I = intervals.get(i); n += (I.b-I.a+1); } return n; } public IntegerList toIntegerList() { IntegerList values = new IntegerList(size()); int n = intervals.size(); for (int i = 0; i < n; i++) { Interval I = intervals.get(i); int a = I.a; int b = I.b; for (int v=a; v<=b; v++) { values.add(v); } } return values; } @Override public List toList() { List values = new ArrayList(); int n = intervals.size(); for (int i = 0; i < n; i++) { Interval I = intervals.get(i); int a = I.a; int b = I.b; for (int v=a; v<=b; v++) { values.add(v); } } return values; } public Set toSet() { Set s = new HashSet(); for (Interval I : intervals) { int a = I.a; int b = I.b; for (int v=a; v<=b; v++) { s.add(v); } } return s; } public int[] toArray() { return toIntegerList().toArray(); } @Override public void remove(int el) { if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet"); int n = intervals.size(); for (int i = 0; i < n; i++) { Interval I = intervals.get(i); int a = I.a; int b = I.b; if ( ela && el