package de.unisiegen.gtitool.ui.model; import java.util.ArrayList; import javax.swing.event.EventListenerList; import de.unisiegen.gtitool.core.entities.DefaultNonterminalSymbol; import de.unisiegen.gtitool.core.entities.DefaultNonterminalSymbolSet; import de.unisiegen.gtitool.core.entities.DefaultProduction; import de.unisiegen.gtitool.core.entities.DefaultTerminalSymbolSet; import de.unisiegen.gtitool.core.entities.NonterminalSymbol; import de.unisiegen.gtitool.core.entities.NonterminalSymbolSet; import de.unisiegen.gtitool.core.entities.Production; import de.unisiegen.gtitool.core.entities.TerminalSymbolSet; import de.unisiegen.gtitool.core.entities.listener.ModifyStatusChangedListener; import de.unisiegen.gtitool.core.exceptions.nonterminalsymbolset.NonterminalSymbolSetException; import de.unisiegen.gtitool.core.exceptions.terminalsymbolset.TerminalSymbolSetException; import de.unisiegen.gtitool.core.grammars.Grammar; import de.unisiegen.gtitool.core.grammars.cfg.DefaultCFG; import de.unisiegen.gtitool.core.grammars.rg.DefaultRG; import de.unisiegen.gtitool.core.i18n.Messages; import de.unisiegen.gtitool.core.machines.Machine; import de.unisiegen.gtitool.core.storage.Attribute; 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; import de.unisiegen.gtitool.ui.redoundo.ProductionsListChangedItem; import de.unisiegen.gtitool.ui.redoundo.RedoUndoHandler; import de.unisiegen.gtitool.ui.redoundo.RedoUndoItem; /** * The Model for the {@link Grammar}s * * @author Benjamin Mies * @version $Id$ */ public class DefaultGrammarModel implements DefaultModel, Storable, Modifyable { /** * The {@link RedoUndoHandler} */ private RedoUndoHandler redoUndoHandler; /** * The {@link Machine} version. */ private static final int GRAMMAR_VERSION = 734; /** * The {@link Grammar}. */ private Grammar grammar; /** * The {@link ModifyStatusChangedListener}. */ private ModifyStatusChangedListener modifyStatusChangedListener; /** * List of listeners */ private EventListenerList listenerList = new EventListenerList (); /** * Allocate a new {@link DefaultGrammarModel}. * * @param element The {@link Element}. * @param overwrittenMachineType The overwritten machine type which is used * instead of the loaded machine type if it is not null. * @throws NonterminalSymbolSetException If something with the * {@link NonterminalSymbolSet} is not correct. * @throws StoreException If the {@link Element} can not be parsed. * @throws TerminalSymbolSetException If something with the * {@link TerminalSymbolSet} is not correct. */ public DefaultGrammarModel ( Element element, String overwrittenMachineType ) throws NonterminalSymbolSetException, StoreException, TerminalSymbolSetException { // Attribute boolean foundGrammarVersion = false; String grammarType = null; for ( Attribute attribute : element.getAttribute () ) { if ( attribute.getName ().equals ( "grammarType" ) ) //$NON-NLS-1$ { if ( overwrittenMachineType == null ) { grammarType = attribute.getValue (); } else { grammarType = overwrittenMachineType; } } else if ( attribute.getName ().equals ( "grammarVersion" ) ) //$NON-NLS-1$ { foundGrammarVersion = true; if ( GRAMMAR_VERSION != attribute.getValueInt () ) { throw new StoreException ( Messages .getString ( "StoreException.IncompatibleVersion" ) ); //$NON-NLS-1$ } } else { throw new StoreException ( Messages .getString ( "StoreException.AdditionalAttribute" ) ); //$NON-NLS-1$ } } NonterminalSymbolSet nonterminalSymbolSet = null; TerminalSymbolSet terminalSymbolSet = null; NonterminalSymbol startSymbol = null; for ( Element current : element.getElement () ) { if ( current.getName ().equals ( "NonterminalSymbolSet" ) ) //$NON-NLS-1$ { nonterminalSymbolSet = new DefaultNonterminalSymbolSet ( current ); } else if ( current.getName ().equals ( "TerminalSymbolSet" ) ) //$NON-NLS-1$ { terminalSymbolSet = new DefaultTerminalSymbolSet ( current ); } else if ( current.getName ().equals ( "NonterminalSymbol" ) ) //$NON-NLS-1$ { startSymbol = new DefaultNonterminalSymbol ( current ); } } if ( ( nonterminalSymbolSet == null ) || ( terminalSymbolSet == null ) || ( grammarType == null ) || ( startSymbol == null ) || !foundGrammarVersion ) { throw new StoreException ( Messages .getString ( "StoreException.MissingAttribute" ) ); //$NON-NLS-1$ } ArrayList < Production > productions = new ArrayList < Production > (); for ( Element current : element.getElement () ) { if ( current.getName ().equals ( "Production" ) ) //$NON-NLS-1$ { productions.add ( new DefaultProduction ( current ) ); } else if ( ( !current.getName ().equals ( "NonterminalSymbolSet" ) ) //$NON-NLS-1$ && ( !current.getName ().equals ( "TerminalSymbolSet" ) ) //$NON-NLS-1$ && ( !current.getName ().equals ( "NonterminalSymbol" ) ) ) //$NON-NLS-1$ { throw new StoreException ( Messages .getString ( "StoreException.AdditionalElement" ) ); //$NON-NLS-1$ } } if ( grammarType.equalsIgnoreCase ( "CFG" ) ) //$NON-NLS-1$ { this.grammar = new DefaultCFG ( nonterminalSymbolSet, terminalSymbolSet, startSymbol ); } else if ( grammarType.equalsIgnoreCase ( "RG" ) ) //$NON-NLS-1$ { this.grammar = new DefaultRG ( nonterminalSymbolSet, terminalSymbolSet, startSymbol ); } else { throw new RuntimeException ( "unsupported grammar type" ); //$NON-NLS-1$ } for ( Production current : productions ) { this.grammar.addProduction ( current ); } this.grammar.resetModify (); initializeModifyStatusChangedListener (); } /** * Allocate a new {@link DefaultGrammarModel}. * * @param grammar The {@link Grammar} */ public DefaultGrammarModel ( Grammar grammar ) { this.grammar = grammar; initializeModifyStatusChangedListener (); } /** * {@inheritDoc} * * @see Machine#addModifyStatusChangedListener(ModifyStatusChangedListener) */ public final void addModifyStatusChangedListener ( ModifyStatusChangedListener listener ) { this.listenerList.add ( ModifyStatusChangedListener.class, listener ); } /** * Adds a new {@link Production} to list. * * @param production The production to add. * @param createUndoStep Flag signals if an undo step should be created. */ public final void addProduction ( Production production, boolean createUndoStep ) { ArrayList < Production > productions = new ArrayList < Production > (); productions.addAll ( this.grammar.getProduction () ); this.grammar.addProduction ( production ); if ( createUndoStep ) { RedoUndoItem item = new ProductionsListChangedItem ( this.grammar, productions ); this.redoUndoHandler.addItem ( item ); } } /** * Let the listeners know that the modify status has changed. * * @param forceModify True if the modify is forced, otherwise false. */ protected final void fireModifyStatusChanged ( boolean forceModify ) { ModifyStatusChangedListener [] listeners = this.listenerList .getListeners ( ModifyStatusChangedListener.class ); if ( forceModify ) { for ( ModifyStatusChangedListener current : listeners ) { current.modifyStatusChanged ( true ); } } else { boolean newModifyStatus = isModified (); for ( ModifyStatusChangedListener current : listeners ) { current.modifyStatusChanged ( newModifyStatus ); } } } /** * {@inheritDoc} * * @see Storable#getElement() */ public final Element getElement () { Element newElement = new Element ( "GrammarModel" ); //$NON-NLS-1$ newElement.addAttribute ( new Attribute ( "grammarType", this.grammar //$NON-NLS-1$ .getGrammarType ().toString () ) ); newElement.addAttribute ( new Attribute ( "grammarVersion", //$NON-NLS-1$ GRAMMAR_VERSION ) ); newElement.addElement ( this.grammar.getStartSymbol ().getElement () ); newElement.addElement ( this.grammar.getNonterminalSymbolSet () .getElement () ); newElement.addElement ( this.grammar.getTerminalSymbolSet ().getElement () ); for ( Production current : this.grammar.getProduction () ) { newElement.addElement ( current.getElement () ); } return newElement; } /** * Returns the {@link Grammar}. * * @return the {@link Grammar}. */ public final Grammar getGrammar () { return this.grammar; } /** * Initializes the {@link ModifyStatusChangedListener}. */ private final void initializeModifyStatusChangedListener () { this.modifyStatusChangedListener = new ModifyStatusChangedListener () { public void modifyStatusChanged ( boolean modified ) { fireModifyStatusChanged ( modified ); } }; this.grammar .addModifyStatusChangedListener ( this.modifyStatusChangedListener ); } /** * {@inheritDoc} * * @see Modifyable#isModified() */ public final boolean isModified () { return this.grammar.isModified (); } /** * {@inheritDoc} * * @see Modifyable#removeModifyStatusChangedListener(ModifyStatusChangedListener) */ public final void removeModifyStatusChangedListener ( ModifyStatusChangedListener listener ) { this.listenerList.remove ( ModifyStatusChangedListener.class, listener ); } /** * Remove a {@link Production} from list. * * @param index The index of the {@link Production}. */ public final void removeProduction ( int index ) { this.grammar.removeProduction ( index ); } /** * {@inheritDoc} * * @see Modifyable#resetModify() */ public final void resetModify () { this.grammar.resetModify (); } /** * Set a new {@link RedoUndoHandler} * * @param redoUndoHandler the new {@link RedoUndoHandler} */ public final void setRedoUndoHandler ( RedoUndoHandler redoUndoHandler ) { this.redoUndoHandler = redoUndoHandler; } }