package de.unisiegen.gtitool.core.entities; import java.util.ArrayList; import java.util.Iterator; import java.util.TreeSet; import javax.swing.event.EventListenerList; import de.unisiegen.gtitool.core.entities.listener.ModifyStatusChangedListener; import de.unisiegen.gtitool.core.entities.listener.PrettyStringChangedListener; import de.unisiegen.gtitool.core.entities.listener.TerminalSymbolSetChangedListener; import de.unisiegen.gtitool.core.exceptions.terminalsymbolset.TerminalSymbolSetException; import de.unisiegen.gtitool.core.exceptions.terminalsymbolset.TerminalSymbolSetMoreThanOneSymbolException; import de.unisiegen.gtitool.core.i18n.Messages; import de.unisiegen.gtitool.core.parser.ParserOffset; import de.unisiegen.gtitool.core.parser.style.PrettyPrintable; import de.unisiegen.gtitool.core.parser.style.PrettyString; import de.unisiegen.gtitool.core.parser.style.PrettyToken; import de.unisiegen.gtitool.core.parser.style.Style; import de.unisiegen.gtitool.core.parser.style.PrettyString.PrettyStringMode; import de.unisiegen.gtitool.core.storage.Element; import de.unisiegen.gtitool.core.storage.Modifyable; import de.unisiegen.gtitool.core.storage.Storable; import de.unisiegen.gtitool.core.storage.exceptions.StoreException; /** * The {@link DefaultTerminalSymbolSet} entity. * * @author Christian Fehler * @version $Id: DefaultTerminalSymbolSet.java 1586 2009-06-29 20:45:10Z fehler * $ */ public final class DefaultTerminalSymbolSet implements TerminalSymbolSet { /** * The serial version uid. */ private static final long serialVersionUID = 2256414069656309793L; /** * Checks the {@link TerminalSymbol}s in the {@link ArrayList} for Classes * * @param list The {@link ArrayList} containing the {@link TerminalSymbol}s to * check * @return {@link ArrayList} with the first class, if there is one, else there * is only one Element in the {@link ArrayList} */ public static ArrayList < TerminalSymbol > checkForClass ( ArrayList < TerminalSymbol > list ) { int dist = 1; int counter = 0; ArrayList < TerminalSymbol > s = new ArrayList < TerminalSymbol > (); char first = list.get ( counter ).getName ().charAt ( 0 ); s.add ( new DefaultTerminalSymbol ( Character.toString ( first ) ) ); while ( dist == 1 ) { char c1 = list.get ( counter ).getName ().charAt ( 0 ); char c2 = 0; if ( counter + 1 != list.size () ) { c2 = list.get ( ++counter ).getName ().charAt ( 0 ); } dist = c2 - c1; if ( dist == 1 ) { s.add ( new DefaultTerminalSymbol ( Character.toString ( c2 ) ) ); } } return s; } /** * The {@link EventListenerList}. */ private EventListenerList listenerList = new EventListenerList (); /** * The offset of this {@link DefaultTerminalSymbolSet} in the source code. * * @see #getParserOffset() * @see #setParserOffset(ParserOffset) */ private ParserOffset parserOffset = NO_PARSER_OFFSET; /** * The set of {@link TerminalSymbol}s. */ private TreeSet < TerminalSymbol > terminalSymbolSet; /** * The initial set of {@link TerminalSymbol}s. */ private TreeSet < TerminalSymbol > initialTerminalSymbolSet; /** * The cached {@link PrettyString}. */ private PrettyString cachedPrettyString = null; /** * The {@link PrettyStringChangedListener}. */ private PrettyStringChangedListener prettyStringChangedListener; /** * Allocates a new {@link DefaultTerminalSymbolSet}. */ public DefaultTerminalSymbolSet () { this.prettyStringChangedListener = new PrettyStringChangedListener () { public void prettyStringChanged () { firePrettyStringChanged (); } }; // TerminalSymbol this.terminalSymbolSet = new TreeSet < TerminalSymbol > (); this.initialTerminalSymbolSet = new TreeSet < TerminalSymbol > (); resetModify (); } /** * Allocates a new {@link DefaultTerminalSymbolSet}. * * @param element The {@link Element}. * @throws TerminalSymbolSetException If something with the * {@link DefaultTerminalSymbolSet} is not correct. * @throws StoreException If the {@link Element} can not be parsed. */ public DefaultTerminalSymbolSet ( Element element ) throws TerminalSymbolSetException, StoreException { this (); // Check if the element is correct if ( !element.getName ().equals ( "TerminalSymbolSet" ) ) //$NON-NLS-1$ { throw new IllegalArgumentException ( "element " + Messages.QUOTE + element.getName () //$NON-NLS-1$ + Messages.QUOTE + " is not a terminal symbol set" ); //$NON-NLS-1$ } // Attribute if ( element.getAttribute ().size () > 0 ) { throw new StoreException ( Messages .getString ( "StoreException.AdditionalAttribute" ) ); //$NON-NLS-1$ } // Element for ( Element current : element.getElement () ) { if ( current.getName ().equals ( "TerminalSymbol" ) ) //$NON-NLS-1$ { add ( new DefaultTerminalSymbol ( current ) ); } else { throw new StoreException ( Messages .getString ( "StoreException.AdditionalElement" ) ); //$NON-NLS-1$ } } resetModify (); } /** * Allocates a new {@link DefaultTerminalSymbolSet}. * * @param terminalSymbols The array of {@link TerminalSymbol}s. * @throws TerminalSymbolSetException If something with the * {@link DefaultTerminalSymbolSet} is not correct. */ public DefaultTerminalSymbolSet ( Iterable < TerminalSymbol > terminalSymbols ) throws TerminalSymbolSetException { this (); // TerminalSymbols if ( terminalSymbols == null ) { throw new NullPointerException ( "symbols is null" ); //$NON-NLS-1$ } add ( terminalSymbols ); resetModify (); } /** * Allocates a new {@link DefaultTerminalSymbolSet}. * * @param terminalSymbols The array of {@link TerminalSymbol}s. * @throws TerminalSymbolSetException If something with the * {@link DefaultTerminalSymbolSet} is not correct. */ public DefaultTerminalSymbolSet ( TerminalSymbol ... terminalSymbols ) throws TerminalSymbolSetException { this (); // TerminalSymbols if ( terminalSymbols == null ) { throw new NullPointerException ( "symbols is null" ); //$NON-NLS-1$ } add ( terminalSymbols ); resetModify (); } /** * {@inheritDoc} * * @see TerminalSymbolSet#add(java.lang.Iterable) */ public final void add ( Iterable < TerminalSymbol > terminalSymbols ) throws TerminalSymbolSetException { if ( terminalSymbols == null ) { throw new NullPointerException ( "terminal symbols is null" ); //$NON-NLS-1$ } ArrayList < TerminalSymbol > symbolList = new ArrayList < TerminalSymbol > (); for ( TerminalSymbol current : terminalSymbols ) { symbolList.add ( current ); } checkDuplicated ( symbolList ); for ( TerminalSymbol current : terminalSymbols ) { add ( current ); } } /** * {@inheritDoc} * * @see TerminalSymbolSet#add(TerminalSymbol) */ public final void add ( TerminalSymbol terminalSymbol ) throws TerminalSymbolSetException { // TerminalSymbol if ( terminalSymbol == null ) { throw new NullPointerException ( "terminal symbol is null" ); //$NON-NLS-1$ } /* * Throws an TerminalSymbolSetException if the symbol which should be added * is already in this TerminalSymbolSet. */ if ( this.terminalSymbolSet.contains ( terminalSymbol ) ) { ArrayList < TerminalSymbol > negativeSymbols = new ArrayList < TerminalSymbol > (); for ( TerminalSymbol current : this.terminalSymbolSet ) { if ( terminalSymbol.equals ( current ) ) { negativeSymbols.add ( current ); } } negativeSymbols.add ( terminalSymbol ); throw new TerminalSymbolSetMoreThanOneSymbolException ( this, negativeSymbols ); } terminalSymbol .addPrettyStringChangedListener ( this.prettyStringChangedListener ); this.terminalSymbolSet.add ( terminalSymbol ); fireTerminalSymbolSetChanged (); fireModifyStatusChanged (); firePrettyStringChanged (); } /** * {@inheritDoc} * * @see TerminalSymbolSet#add(TerminalSymbol[]) */ public final void add ( TerminalSymbol ... terminalSymbols ) throws TerminalSymbolSetException { if ( terminalSymbols == null ) { throw new NullPointerException ( "terminal symbols is null" ); //$NON-NLS-1$ } ArrayList < TerminalSymbol > symbolList = new ArrayList < TerminalSymbol > (); for ( TerminalSymbol current : terminalSymbols ) { symbolList.add ( current ); } checkDuplicated ( symbolList ); for ( TerminalSymbol current : terminalSymbols ) { add ( current ); } } /** * {@inheritDoc} * * @see Modifyable#addModifyStatusChangedListener(ModifyStatusChangedListener) */ public final void addModifyStatusChangedListener ( ModifyStatusChangedListener listener ) { this.listenerList.add ( ModifyStatusChangedListener.class, listener ); } /** * {@inheritDoc} * * @see PrettyPrintable#addPrettyStringChangedListener(PrettyStringChangedListener) */ public final void addPrettyStringChangedListener ( PrettyStringChangedListener listener ) { this.listenerList.add ( PrettyStringChangedListener.class, listener ); } /** * {@inheritDoc} * * @see TerminalSymbolSet#addTerminalSymbolSetChangedListener(TerminalSymbolSetChangedListener) */ public final void addTerminalSymbolSetChangedListener ( TerminalSymbolSetChangedListener listener ) { this.listenerList.add ( TerminalSymbolSetChangedListener.class, listener ); } /** * Checks the {@link TerminalSymbol} list for {@link TerminalSymbol}s with the * same name. * * @param terminalSymbols The {@link TerminalSymbol} list. * @throws TerminalSymbolSetException If a {@link TerminalSymbol} is * duplicated. */ private final void checkDuplicated ( ArrayList < TerminalSymbol > terminalSymbols ) throws TerminalSymbolSetException { TerminalSymbol duplicated = null; loop : for ( int i = 0 ; i < terminalSymbols.size () ; i++ ) { for ( int j = i + 1 ; j < terminalSymbols.size () ; j++ ) { if ( terminalSymbols.get ( i ).equals ( terminalSymbols.get ( j ) ) ) { duplicated = terminalSymbols.get ( i ); break loop; } } } if ( duplicated != null ) { ArrayList < TerminalSymbol > negativeSymbols = new ArrayList < TerminalSymbol > (); for ( TerminalSymbol current : terminalSymbols ) { if ( duplicated.equals ( current ) ) { negativeSymbols.add ( current ); } } throw new TerminalSymbolSetMoreThanOneSymbolException ( this, negativeSymbols ); } } /** * {@inheritDoc} * * @see TerminalSymbolSet#clear() */ public final void clear () { for ( TerminalSymbol current : this.terminalSymbolSet ) { current .removePrettyStringChangedListener ( this.prettyStringChangedListener ); } this.terminalSymbolSet.clear (); fireTerminalSymbolSetChanged (); fireModifyStatusChanged (); } /** * {@inheritDoc} * * @see Comparable#compareTo(Object) */ public final int compareTo ( TerminalSymbolSet other ) { ArrayList < TerminalSymbol > firstList = new ArrayList < TerminalSymbol > (); ArrayList < TerminalSymbol > secondList = new ArrayList < TerminalSymbol > (); firstList.addAll ( this.terminalSymbolSet ); secondList.addAll ( other.get () ); int minSize = firstList.size () < secondList.size () ? firstList.size () : secondList.size (); for ( int i = 0 ; i < minSize ; i++ ) { int compare = firstList.get ( i ).compareTo ( secondList.get ( i ) ); if ( compare != 0 ) { return compare; } } if ( firstList.size () == secondList.size () ) { return 0; } return firstList.size () < secondList.size () ? -1 : 1; } /** * {@inheritDoc} * * @see TerminalSymbolSet#contains(TerminalSymbol) */ public final boolean contains ( TerminalSymbol terminalSymbol ) { return this.terminalSymbolSet.contains ( terminalSymbol ); } /** * {@inheritDoc} * * @see Object#equals(Object) */ @Override public final boolean equals ( Object other ) { if ( other instanceof DefaultTerminalSymbolSet ) { DefaultTerminalSymbolSet defaultTerminalSymbolSet = ( DefaultTerminalSymbolSet ) other; return this.terminalSymbolSet .equals ( defaultTerminalSymbolSet.terminalSymbolSet ); } return false; } /** * Let the listeners know that the modify status has changed. */ private final void fireModifyStatusChanged () { ModifyStatusChangedListener [] listeners = this.listenerList .getListeners ( ModifyStatusChangedListener.class ); boolean newModifyStatus = isModified (); for ( ModifyStatusChangedListener current : listeners ) { current.modifyStatusChanged ( newModifyStatus ); } } /** * Let the listeners know that the {@link PrettyString} has changed. */ protected final void firePrettyStringChanged () { this.cachedPrettyString = null; PrettyStringChangedListener [] listeners = this.listenerList .getListeners ( PrettyStringChangedListener.class ); for ( PrettyStringChangedListener current : listeners ) { current.prettyStringChanged (); } } /** * Let the listeners know that the {@link TerminalSymbolSet} has changed. */ private final void fireTerminalSymbolSetChanged () { TerminalSymbolSetChangedListener [] listeners = this.listenerList .getListeners ( TerminalSymbolSetChangedListener.class ); for ( TerminalSymbolSetChangedListener current : listeners ) { current.terminalSymbolSetChanged ( this ); } } /** * {@inheritDoc} * * @see TerminalSymbolSet#get() */ public final TreeSet < TerminalSymbol > get () { return this.terminalSymbolSet; } /** * {@inheritDoc} * * @see TerminalSymbolSet#get(int) */ public final TerminalSymbol get ( int index ) { Iterator < TerminalSymbol > iterator = this.terminalSymbolSet.iterator (); for ( int i = 0 ; i < index ; i++ ) { iterator.next (); } return iterator.next (); } /** * {@inheritDoc} * * @see Storable#getElement() */ public final Element getElement () { Element newElement = new Element ( "TerminalSymbolSet" ); //$NON-NLS-1$ for ( TerminalSymbol current : this.terminalSymbolSet ) { newElement.addElement ( current ); } return newElement; } /** * {@inheritDoc} * * @see Entity#getParserOffset() */ public final ParserOffset getParserOffset () { return this.parserOffset; } /** * {@inheritDoc} * * @see Entity#hashCode() */ @Override public final int hashCode () { return this.terminalSymbolSet.hashCode (); } /** * {@inheritDoc} * * @see Modifyable#isModified() */ public final boolean isModified () { return ( !this.terminalSymbolSet.equals ( this.initialTerminalSymbolSet ) ); } /** * {@inheritDoc} * * @see Iterable#iterator() */ public final Iterator < TerminalSymbol > iterator () { return this.terminalSymbolSet.iterator (); } /** * {@inheritDoc} * * @see TerminalSymbolSet#remove(Iterable) */ public final void remove ( Iterable < TerminalSymbol > terminalSymbols ) { if ( terminalSymbols == null ) { throw new NullPointerException ( "terminal symbols is null" ); //$NON-NLS-1$ } for ( TerminalSymbol current : terminalSymbols ) { remove ( current ); } } /** * {@inheritDoc} * * @see TerminalSymbolSet#remove(TerminalSymbol) */ public final void remove ( TerminalSymbol terminalSymbol ) { if ( terminalSymbol == null ) { throw new NullPointerException ( "terminal symbol is null" ); //$NON-NLS-1$ } if ( !this.terminalSymbolSet.contains ( terminalSymbol ) ) { throw new IllegalArgumentException ( "terminal symbol is not in this terminal symbol set" ); //$NON-NLS-1$ } terminalSymbol .removePrettyStringChangedListener ( this.prettyStringChangedListener ); this.terminalSymbolSet.remove ( terminalSymbol ); fireTerminalSymbolSetChanged (); fireModifyStatusChanged (); firePrettyStringChanged (); } /** * {@inheritDoc} * * @see TerminalSymbolSet#remove(TerminalSymbol[]) */ public final void remove ( TerminalSymbol ... terminalSymbols ) { if ( terminalSymbols == null ) { throw new NullPointerException ( "terminal symbols is null" ); //$NON-NLS-1$ } for ( TerminalSymbol current : terminalSymbols ) { remove ( current ); } } /** * {@inheritDoc} * * @see Modifyable#removeModifyStatusChangedListener(ModifyStatusChangedListener) */ public final void removeModifyStatusChangedListener ( ModifyStatusChangedListener listener ) { this.listenerList.remove ( ModifyStatusChangedListener.class, listener ); } /** * {@inheritDoc} * * @see PrettyPrintable#removePrettyStringChangedListener(PrettyStringChangedListener) */ public final void removePrettyStringChangedListener ( PrettyStringChangedListener listener ) { this.listenerList.remove ( PrettyStringChangedListener.class, listener ); } /** * {@inheritDoc} * * @see TerminalSymbolSet#removeTerminalSymbolSetChangedListener(TerminalSymbolSetChangedListener) */ public final void removeTerminalSymbolSetChangedListener ( TerminalSymbolSetChangedListener listener ) { this.listenerList .remove ( TerminalSymbolSetChangedListener.class, listener ); } /** * {@inheritDoc} * * @see Modifyable#resetModify() */ public final void resetModify () { this.initialTerminalSymbolSet.clear (); this.initialTerminalSymbolSet.addAll ( this.terminalSymbolSet ); } /** * {@inheritDoc} * * @see Entity#setParserOffset(de.unisiegen.gtitool.core.parser.ParserOffset) */ public final void setParserOffset ( ParserOffset parserOffset ) { this.parserOffset = parserOffset; } /** * {@inheritDoc} * * @see TerminalSymbolSet#size() */ public final int size () { return this.terminalSymbolSet.size (); } /** * {@inheritDoc} * * @see PrettyPrintable#toPrettyString() */ public final PrettyString toPrettyString () { if ( ( this.cachedPrettyString == null ) || PrettyString.MODE.equals ( PrettyStringMode.CACHING_OFF ) ) { this.cachedPrettyString = new PrettyString (); this.cachedPrettyString.add ( new PrettyToken ( "{", Style.NONE ) ); //$NON-NLS-1$ boolean first = true; ArrayList < TerminalSymbol > t = new ArrayList < TerminalSymbol > (); t.addAll ( this.terminalSymbolSet ); while ( !t.isEmpty () ) { ArrayList < TerminalSymbol > a = checkForClass ( t ); if ( !first ) { this.cachedPrettyString.add ( new PrettyToken ( ", ", Style.NONE ) ); //$NON-NLS-1$ } first = false; if ( a.size () == 1 ) { this.cachedPrettyString.add ( a.get ( 0 ) ); } else if ( a.size () == 2 ) { this.cachedPrettyString.add ( a.get ( 0 ) ); this.cachedPrettyString.add ( new PrettyToken ( ", ", Style.NONE ) ); //$NON-NLS-1$ this.cachedPrettyString.add ( a.get ( 1 ) ); } else { this.cachedPrettyString.add ( a.get ( 0 ) ); this.cachedPrettyString.add ( new PrettyToken ( "..", Style.NONE ) ); //$NON-NLS-1$ this.cachedPrettyString.add ( a.get ( a.size () - 1 ) ); } t.removeAll ( a ); } this.cachedPrettyString.add ( new PrettyToken ( "}", Style.NONE ) ); //$NON-NLS-1$ } return this.cachedPrettyString; } /** * {@inheritDoc} * * @see Entity#toString() */ @Override public final String toString () { StringBuilder result = new StringBuilder (); result.append ( "{" ); //$NON-NLS-1$ boolean first = true; ArrayList < TerminalSymbol > t = new ArrayList < TerminalSymbol > (); t.addAll ( this.terminalSymbolSet ); while ( !t.isEmpty () ) { ArrayList < TerminalSymbol > a = checkForClass ( t ); if ( !first ) { result.append ( new PrettyToken ( ", ", Style.NONE ) ); //$NON-NLS-1$ } first = false; if ( a.size () == 1 ) { result.append ( a.get ( 0 ) ); } else if ( a.size () == 2 ) { result.append ( a.get ( 0 ) ); result.append ( new PrettyToken ( ", ", Style.NONE ) ); //$NON-NLS-1$ result.append ( a.get ( 1 ) ); } else { result.append ( new PrettyToken ( "[", Style.SYMBOL ) ); //$NON-NLS-1$ result.append ( a.get ( 0 ) ); result.append ( new PrettyToken ( "-", Style.NONE ) ); //$NON-NLS-1$ result.append ( a.get ( a.size () - 1 ) ); result.append ( new PrettyToken ( "]", Style.SYMBOL ) ); //$NON-NLS-1$ } t.removeAll ( a ); } result.append ( "}" ); //$NON-NLS-1$ return result.toString (); } }