/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This file is part of SableCC. * * See the file "LICENSE" for copyright information and the * * terms and conditions for copying, distribution and * * modification of SableCC. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package org.sablecc.sablecc; import java.util.Enumeration; import java.util.Vector; @SuppressWarnings({"rawtypes", "unchecked"}) public class CharSet implements Cloneable { private final Vector intervals = new Vector(0); public CharSet(char c) { intervals.addElement(new Interval(c, c)); } public CharSet(char start, char end) { intervals.addElement(new Interval(start, end)); } private CharSet(Vector intervals) { for (Enumeration e = intervals.elements(); e.hasMoreElements(); ) { this.intervals.addElement(((Interval) e.nextElement()).clone()); } } @Override public Object clone() { return new CharSet(intervals); } public Interval findOverlap(Interval interval1) { int low = 0; int high = intervals.size() - 1; Interval interval2; Interval result = null; while (high >= low) { int middle = (high + low) / 2; interval2 = (Interval) intervals.elementAt(middle); if (interval1.start <= interval2.end) { if (interval1.end >= interval2.start) { result = interval2; // we continue, to find the lowest matching interval! } high = middle - 1; } else { low = middle + 1; } } return result; } private void remove (Interval interval) { intervals.removeElement(interval); } private void add (Interval interval) { for (int i = 0; i < intervals.size(); i++) { Interval iv = (Interval) intervals.elementAt(i); if (iv.start > interval.start) { intervals.insertElementAt(interval, i); return; } } intervals.addElement(interval); } public CharSet union(CharSet chars) { CharSet result = (CharSet) clone(); Interval interval; Interval largeInterval; Interval overlap; for (Enumeration e = chars.intervals.elements(); e.hasMoreElements(); ) { interval = (Interval) ((Interval) e.nextElement()).clone(); do { largeInterval = new Interval( (interval.start == 0) ? (char) 0 : (char) (interval.start - 1), (interval.end == 0xffff) ? (char) 0xffff : (char) (interval.end + 1)); overlap = result.findOverlap(largeInterval); if (overlap != null) { result.remove(overlap); interval.start = (char) Math.min(interval.start, overlap.start); interval.end = (char) Math.max(interval.end, overlap.end); } } while (overlap != null); result.add(interval); } return result; } public CharSet diff(CharSet chars) { CharSet result = (CharSet) clone(); Interval interval; Interval overlap; for (Enumeration e = chars.intervals.elements(); e.hasMoreElements(); ) { interval = (Interval) ((Interval) e.nextElement()).clone(); do { overlap = result.findOverlap(interval); if (overlap != null) { result.remove(overlap); if (overlap.start < interval.start) { result.add(new Interval(overlap.start, (char) (interval.start - 1))); } if (overlap.end > interval.end) { result.add(new Interval((char) (interval.end + 1), overlap.end)); } } } while (overlap != null); } return result; } @Override public String toString() { StringBuffer result = new StringBuffer(); for (Enumeration e = intervals.elements(); e.hasMoreElements(); ) { result.append("[" + e.nextElement() + "] "); } return "" + result; } public static class Interval implements Cloneable { public Interval(char start, char end) { this.start = start; this.end = end; } @Override public Object clone() { return new Interval(start, end); } private String c(char c) { if ((c >= 32) && (c < 127)) { return "" + c; } return "" + ((int) c); } @Override public String toString() { if (start < end) { return c(start) + " .. " + c(end); } else { return c(start); } } public char start; public char end; } public static class IntervalCast implements Cast { public final static IntervalCast instance = new IntervalCast(); private IntervalCast() { } @Override public Object cast(Object o) { return (Interval) o; } } }