package de.unisiegen.gtitool.core.entities;
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.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 DefaultProduction} entity.
*
* @author Christian Fehler
* @version $Id$
*/
public final class DefaultProduction implements Production
{
/**
* The serial version uid.
*/
private static final long serialVersionUID = -4383623142476990175L;
/**
* This {@link Production} is a error {@link Production}.
*/
private boolean error = false;
/**
* This {@link Production} is a highlighted {@link Production}.
*/
private boolean highlighted = false;
/**
* The {@link EventListenerList}.
*/
private EventListenerList listenerList = new EventListenerList ();
/**
* The offset of this {@link DefaultProduction} in the source code.
*
* @see #getParserOffset()
* @see #setParserOffset(ParserOffset)
*/
private ParserOffset parserOffset = NO_PARSER_OFFSET;
/**
* The {@link NonterminalSymbol}
*/
private NonterminalSymbol nonterminalSymbol;
/**
* The initial {@link NonterminalSymbol}
*/
private NonterminalSymbol initialNonterminalSymbol;
/**
* The {@link ProductionWord}
*/
private ProductionWord productionWord;
/**
* The initial {@link ProductionWord}.
*/
private ProductionWord initialProductionWord;
/**
* The cached {@link PrettyString}.
*/
private PrettyString cachedPrettyString = null;
/**
* The {@link PrettyStringChangedListener}.
*/
private PrettyStringChangedListener prettyStringChangedListener;
/**
* Allocates a new {@link DefaultProduction}.
*
* @param element The {@link Element}.
* @throws StoreException If the {@link Element} can not be parsed.
*/
public DefaultProduction ( Element element ) throws StoreException
{
this.prettyStringChangedListener = new PrettyStringChangedListener ()
{
public void prettyStringChanged ()
{
firePrettyStringChanged ();
}
};
for ( Element current : element.getElement () )
{
if ( current.getName ().equals ( "NonterminalSymbol" ) ) //$NON-NLS-1$
{
setNonterminalSymbol ( new DefaultNonterminalSymbol ( current ) );
}
else if ( current.getName ().equals ( "ProductionWord" ) ) //$NON-NLS-1$
{
setProductionWord ( new DefaultProductionWord ( current ) );
}
else
{
throw new StoreException ( Messages
.getString ( "StoreException.AdditionalElement" ) ); //$NON-NLS-1$
}
}
if ( ( this.nonterminalSymbol == null ) || ( this.productionWord == null ) )
{
throw new StoreException ( Messages
.getString ( "StoreException.MissingAttribute" ) ); //$NON-NLS-1$
}
resetModify ();
}
/**
* Allocates a new {@link DefaultProduction}.
*
* @param nonterminalSymbol The {@link NonterminalSymbol}.
* @param productionWord The {@link ProductionWord}.
*/
public DefaultProduction ( NonterminalSymbol nonterminalSymbol,
ProductionWord productionWord )
{
this.prettyStringChangedListener = new PrettyStringChangedListener ()
{
public void prettyStringChanged ()
{
firePrettyStringChanged ();
}
};
setNonterminalSymbol ( nonterminalSymbol );
setProductionWord ( productionWord );
resetModify ();
}
/**
* {@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 Comparable#compareTo(Object)
*/
public final int compareTo ( Production other )
{
// NonterminalSymbol
int compare = this.nonterminalSymbol.compareTo ( other
.getNonterminalSymbol () );
if ( compare != 0 )
{
return compare;
}
// ProductionWord
return this.productionWord.compareTo ( other.getProductionWord () );
}
/**
* {@inheritDoc}
*
* @see Production#contains(NonterminalSymbol)
*/
public boolean contains ( NonterminalSymbol symbol )
{
for ( ProductionWordMember current : this.productionWord )
{
if ( current.equals ( symbol ) )
{
return true;
}
}
return this.nonterminalSymbol.equals ( symbol );
}
/**
* {@inheritDoc}
*
* @see Production#contains(TerminalSymbol)
*/
public boolean contains ( TerminalSymbol symbol )
{
for ( ProductionWordMember current : this.productionWord )
{
if ( current.equals ( symbol ) )
{
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*
* @see Object#equals(Object)
*/
@Override
public final boolean equals ( Object other )
{
if ( other instanceof DefaultProduction )
{
DefaultProduction defaultProduction = ( DefaultProduction ) other;
if ( this.nonterminalSymbol.equals ( defaultProduction
.getNonterminalSymbol () )
&& this.productionWord.equals ( defaultProduction
.getProductionWord () ) )
{
return true;
}
}
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 ();
}
}
/**
* {@inheritDoc}
*
* @see Storable#getElement()
*/
public final Element getElement ()
{
Element newElement = new Element ( "Production" ); //$NON-NLS-1$
newElement.addElement ( this.nonterminalSymbol.getElement () );
newElement.addElement ( this.productionWord.getElement () );
return newElement;
}
/**
* {@inheritDoc}
*
* @see Production#getNonterminalSymbol()
*/
public NonterminalSymbol getNonterminalSymbol ()
{
return this.nonterminalSymbol;
}
/**
* {@inheritDoc}
*
* @see Entity#getParserOffset()
*/
public final ParserOffset getParserOffset ()
{
return this.parserOffset;
}
/**
* {@inheritDoc}
*
* @see Production#getProductionWord()
*/
public final ProductionWord getProductionWord ()
{
return this.productionWord;
}
/**
* {@inheritDoc}
*
* @see Object#hashCode()
*/
@Override
public final int hashCode ()
{
return this.nonterminalSymbol.hashCode () + this.productionWord.hashCode ();
}
/**
* {@inheritDoc}
*
* @see Production#isError()
*/
public final boolean isError ()
{
return this.error;
}
/**
* {@inheritDoc}
*
* @see Production#isHighlighted()
*/
public boolean isHighlighted ()
{
return this.highlighted;
}
/**
* {@inheritDoc}
*
* @see Modifyable#isModified()
*/
public final boolean isModified ()
{
if ( ( this.productionWord != null )
&& !this.productionWord.equals ( this.initialProductionWord ) )
{
return true;
}
if ( ( this.nonterminalSymbol != null )
&& !this.nonterminalSymbol.equals ( this.initialNonterminalSymbol ) )
{
return true;
}
return false;
}
/**
* {@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 Modifyable#resetModify()
*/
public final void resetModify ()
{
this.initialNonterminalSymbol = new DefaultNonterminalSymbol (
this.nonterminalSymbol.getName () );
this.initialProductionWord = new DefaultProductionWord ();
this.initialProductionWord.add ( this.productionWord );
}
/**
* {@inheritDoc}
*
* @see Production#setError(boolean)
*/
public final void setError ( boolean error )
{
if ( this.error != error )
{
this.error = error;
firePrettyStringChanged ();
}
}
/**
* {@inheritDoc}
*
* @see Production#setHighlighted(boolean)
*/
public void setHighlighted ( boolean highlighted )
{
if ( this.highlighted != highlighted )
{
this.highlighted = highlighted;
firePrettyStringChanged ();
}
}
/**
* {@inheritDoc}
*
* @see Production#setNonterminalSymbol(NonterminalSymbol)
*/
public void setNonterminalSymbol ( NonterminalSymbol nonterminalSymbol )
{
if ( this.nonterminalSymbol == null )
{
nonterminalSymbol
.addPrettyStringChangedListener ( this.prettyStringChangedListener );
this.nonterminalSymbol = nonterminalSymbol;
fireModifyStatusChanged ();
firePrettyStringChanged ();
return;
}
if ( !this.nonterminalSymbol.equals ( nonterminalSymbol ) )
{
this.nonterminalSymbol
.removePrettyStringChangedListener ( this.prettyStringChangedListener );
nonterminalSymbol
.addPrettyStringChangedListener ( this.prettyStringChangedListener );
this.nonterminalSymbol = nonterminalSymbol;
fireModifyStatusChanged ();
firePrettyStringChanged ();
}
}
/**
* {@inheritDoc}
*
* @see Entity#setParserOffset(ParserOffset)
*/
public final void setParserOffset ( ParserOffset parserOffset )
{
this.parserOffset = parserOffset;
}
/**
* {@inheritDoc}
*
* @see Production#setProductionWord(ProductionWord)
*/
public void setProductionWord ( ProductionWord productionWord )
{
if ( this.productionWord == null )
{
productionWord
.addPrettyStringChangedListener ( this.prettyStringChangedListener );
this.productionWord = productionWord;
fireModifyStatusChanged ();
firePrettyStringChanged ();
return;
}
if ( !this.productionWord.equals ( productionWord ) )
{
this.productionWord
.removePrettyStringChangedListener ( this.prettyStringChangedListener );
productionWord
.addPrettyStringChangedListener ( this.prettyStringChangedListener );
this.productionWord = productionWord;
fireModifyStatusChanged ();
firePrettyStringChanged ();
}
}
/**
* {@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 ( this.nonterminalSymbol );
this.cachedPrettyString.add ( new PrettyToken ( " ", Style.NONE ) ); //$NON-NLS-1$
if ( this.error )
{
this.cachedPrettyString.add ( new PrettyToken ( "\u2192", //$NON-NLS-1$
Style.PRODUCTION_ERROR ) );
}
else if ( this.highlighted )
{
this.cachedPrettyString.add ( new PrettyToken ( "\u2192", //$NON-NLS-1$
Style.PRODUCTION_HIGHLIGHT ) );
}
else
{
this.cachedPrettyString.add ( new PrettyToken ( "\u2192", Style.NONE ) ); //$NON-NLS-1$
}
this.cachedPrettyString.add ( new PrettyToken ( " ", Style.NONE ) ); //$NON-NLS-1$
this.cachedPrettyString.add ( this.productionWord );
}
return this.cachedPrettyString;
}
/**
* {@inheritDoc}
*
* @see Entity#toString()
*/
@Override
public final String toString ()
{
return this.nonterminalSymbol.toString () + " \u2192 " //$NON-NLS-1$
+ this.productionWord.toString ();
}
}