eu.mihosoft.vmf.vmftext.grammar.impl.GrammarModelImpl Maven / Gradle / Ivy
The newest version!
package eu.mihosoft.vmf.vmftext.grammar.impl;
// vmf imports
//import eu.mihosoft.vmf.runtime.core.*;
//import eu.mihosoft.vmf.runtime.core.internal.*;
import eu.mihosoft.vcollections.*;
import eu.mihosoft.vmf.vmftext.grammar.*;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;
import java.util.Objects;
import java.util.Arrays;
// property types imports
import eu.mihosoft.vcollections.*;
// implementation
/**
* An implementation of the model object {@code eu.mihosoft.vmf.vmftext.grammar.GrammarModel}.
*/
@SuppressWarnings("deprecation")
class GrammarModelImpl implements GrammarModel, eu.mihosoft.vmf.runtime.core.internal.VObjectInternalModifiable, VCloneableInternal {
// --------------------------------------------------------------------
// --- declaration of member variables
// --------------------------------------------------------------------
/*package private*/ eu.mihosoft.vcollections.VList customRules;
/*package private*/ java.lang.String grammarName;
/*package private*/ java.lang.String packageName;
/*package private*/ eu.mihosoft.vcollections.VList ruleClasses;
/*package private*/ eu.mihosoft.vmf.vmftext.grammar.TypeMappings typeMappings;
// --------------------------------------------------------------------
// --- declaration of delegation variables
// --------------------------------------------------------------------
private eu.mihosoft.vmf.vmftext.grammar.GetRootClassDelegate _vmf_delegate1;
private eu.mihosoft.vmf.vmftext.grammar.RuleClassLookup _vmf_delegate2;
private eu.mihosoft.vmf.vmftext.grammar.CheckRulesDelegate _vmf_delegate3;
private PropertyChangeSupport propertyChanges;
// referenced by
private final VList referencedBy = VList.newInstance(new java.util.ArrayList<>());
// references
private final VList references = VList.newInstance(new java.util.ArrayList<>());
// --------------------------------------------------------------------
// --- public constructors
// --------------------------------------------------------------------
public GrammarModelImpl() {
// --------------------------------------------------------------------
// --- declaration of delegation methods
// --------------------------------------------------------------------
if(_vmf_delegate3==null) {
_vmf_delegate3 = new eu.mihosoft.vmf.vmftext.grammar.CheckRulesDelegate();
_vmf_delegate3.setCaller(this);
}
_vmf_delegate3.onGrammarModelInstantiated();
// --------------------------------------------------------------------
// --- initialization of default values
// --------------------------------------------------------------------
// property 0
// -> no default value is present
// property 1
// -> no default value is present
// property 2
// -> no default value is present
// property 3
// -> no default value is present
// property 4
// -> no default value is present
}
// --------------------------------------------------------------------
// --- public getter methods for accessing properties
// --------------------------------------------------------------------
@Override
public eu.mihosoft.vcollections.VList getCustomRules() {
if(this.customRules==null) {
this.customRules = VList.newInstance(new java.util.ArrayList<>());
this.customRules.addChangeListener((evt)-> {
evt.added().elements().forEach((e)->{
if(e==null) return;
// remove element from previous collection
// TODO use bulk operations if possible
if(e.getModel()!=null) {
e.getModel().getCustomRules().remove(e);
}
try {
java.lang.reflect.Field field =
e.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(e);
field.set(e, this);
// not generating change event (disableFireEventValue=true)
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
if(e!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(e);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)e)._vmf_referencedBy().add(this);
}
}); // end of evt.added()
evt.removed().elements().forEach((e)->{
if(e==null) return;
try {
java.lang.reflect.Field field =
e.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(e);
field.set(e, null);
// not generating change event (disableFireEventValue=true)
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
if(e!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(e);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)e)._vmf_referencedBy().add(this);
}
}); // end of evt.removed()
}); // end of change listener
} // end of "lazy init" if(this.customRules==null)
return this.customRules;
// return VContainmentUtil.asContainmentList(this.customRules, "model");
} // end of eu.mihosoft.vcollections.VList getCustomRules()
@Override
public java.lang.String getGrammarName() {
return this.grammarName;
} // end of java.lang.String getGrammarName()
@Override
public java.lang.String getPackageName() {
return this.packageName;
} // end of java.lang.String getPackageName()
@Override
public eu.mihosoft.vcollections.VList getRuleClasses() {
if(this.ruleClasses==null) {
this.ruleClasses = VList.newInstance(new java.util.ArrayList<>());
this.ruleClasses.addChangeListener((evt)-> {
evt.added().elements().forEach((e)->{
if(e==null) return;
// remove element from previous collection
// TODO use bulk operations if possible
if(e.getModel()!=null) {
e.getModel().getRuleClasses().remove(e);
}
try {
java.lang.reflect.Field field =
e.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(e);
field.set(e, this);
// not generating change event (disableFireEventValue=true)
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
if(e!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(e);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)e)._vmf_referencedBy().add(this);
}
}); // end of evt.added()
evt.removed().elements().forEach((e)->{
if(e==null) return;
try {
java.lang.reflect.Field field =
e.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(e);
field.set(e, null);
// not generating change event (disableFireEventValue=true)
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
if(e!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(e);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)e)._vmf_referencedBy().add(this);
}
}); // end of evt.removed()
}); // end of change listener
} // end of "lazy init" if(this.ruleClasses==null)
return this.ruleClasses;
// return VContainmentUtil.asContainmentList(this.ruleClasses, "model");
} // end of eu.mihosoft.vcollections.VList getRuleClasses()
@Override
public eu.mihosoft.vmf.vmftext.grammar.TypeMappings getTypeMappings() {
return this.typeMappings;
} // end of eu.mihosoft.vmf.vmftext.grammar.TypeMappings getTypeMappings()
// --------------------------------------------------------------------
// --- public setter methods for setting properties
// --------------------------------------------------------------------
@Override
public void setGrammarName(java.lang.String grammarName) {
// return early if identical value has been set
if (this.grammarName == grammarName) {
return;
}
// set the new value
java.lang.String oldValue = this.grammarName;
this.grammarName = grammarName;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("grammarName", oldValue, this.grammarName);
} // setterDeclaration (setter method)
@Override
public void setPackageName(java.lang.String packageName) {
// return early if identical value has been set
if (this.packageName == packageName) {
return;
}
// set the new value
java.lang.String oldValue = this.packageName;
this.packageName = packageName;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("packageName", oldValue, this.packageName);
} // setterDeclaration (setter method)
@Override
public void setTypeMappings(eu.mihosoft.vmf.vmftext.grammar.TypeMappings typeMappings) {
// return early if identical value has been set
if (this.typeMappings == typeMappings) {
return;
}
// set the new value
eu.mihosoft.vmf.vmftext.grammar.TypeMappings oldValue = this.typeMappings;
this.typeMappings = typeMappings;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("typeMappings", oldValue, this.typeMappings);
// if previous container is present then release containment relation
if(oldValue!=null) {
//((eu.mihosoft.vmf.vmftext.grammar.TypeMappingsImpl)oldValue)._vmf_setModelNoContainment(null);
try {
java.lang.reflect.Field field =
oldValue.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(oldValue);
field.set(oldValue, null);
// generate change event (disableFireEventValue=false)
java.lang.reflect.Method m = oldValue.getClass().getDeclaredMethod(
"_vmf_firePropertyChangeIfListenersArePresent",
String.class, Object.class, Object.class);
m.setAccessible(true);
m.invoke(oldValue,
"model", oldOpposite, null);
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
}
// if new container is present then update containment relation
if(typeMappings!=null) {
//((eu.mihosoft.vmf.vmftext.grammar.TypeMappingsImpl)typeMappings)._vmf_setModelNoContainment(this);
// remove 'typeMappings' object that shall be set from its previous parent
// if it has one. for collections we perform this operation in the getter
// method which registers a change listener that does all the work.
if(typeMappings.getModel()!=null) {
typeMappings.getModel().setTypeMappings(null);
}
try {
java.lang.reflect.Field field =
this.typeMappings.getClass().getDeclaredField("model");
field.setAccessible(true);
Object oldOpposite = field.get(this.typeMappings);
field.set(this.typeMappings, this);
// generate change event (disableFireEventValue=false)
java.lang.reflect.Method m = this.typeMappings.getClass().getDeclaredMethod(
"_vmf_firePropertyChangeIfListenersArePresent",
String.class, Object.class, Object.class);
m.setAccessible(true);
m.invoke(this.typeMappings,
"model", oldOpposite, this);
} catch (Exception ex) {
java.util.logging.Logger.getLogger(
GrammarModelImpl.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
}
if(this.typeMappings!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(this.typeMappings);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this.typeMappings)._vmf_referencedBy().add(this);
}
if(oldValue!=null && this!=null) {
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)this)._vmf_references().add(oldValue);
((eu.mihosoft.vmf.runtime.core.internal.VObjectInternal)oldValue)._vmf_referencedBy().add(this);
}
} // setterDeclaration (setter method)
// internal API
void _vmf_setTypeMappingsNoContainment(eu.mihosoft.vmf.vmftext.grammar.TypeMappings typeMappings) {
// set the new value
eu.mihosoft.vmf.vmftext.grammar.TypeMappings oldValue = this.typeMappings;
this.typeMappings = typeMappings;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("typeMappings", oldValue, this.typeMappings);
}
// --------------------------------------------------------------------
// --- Object methods (equals(), toString() etc.)
// --------------------------------------------------------------------
// --------------------------- BEGIN TO_STRING -----------------------------
@Override
public String toString() {
boolean entry = _vmf_getThreadLocalToString().get().isEmpty();
try {
// test if "this" has been seen before
// implementation based on http://stackoverflow.com/a/11300376/1493549
boolean isImmutable = (this instanceof eu.mihosoft.vmf.runtime.core.Immutable);
if (!isImmutable && _vmf_getThreadLocalToString().get().containsKey(this)) {
return "{skipping recursion}";
} else {
if(!isImmutable) {
_vmf_getThreadLocalToString().get().put(this, null);
}
entry = true;
}
return "{\"@type\":\"GrammarModel\"" +
", " + (this.customRules==null?"[]":this.customRules) +
", \"grammarName\": \"" + this.grammarName + "\"" +
", \"packageName\": \"" + this.packageName + "\"" +
", " + (this.ruleClasses==null?"[]":this.ruleClasses) +
", \"typeMappings\": \"" + this.typeMappings + "\"" +
"}";
} finally {
if (entry) {
_vmf_getThreadLocalToString().get().clear();
_vmf_fToStringChecker = null;
}
}
}
private ThreadLocal> _vmf_getThreadLocalToString() {
if (_vmf_fToStringChecker==null) {
_vmf_fToStringChecker = ThreadLocal.withInitial(
() -> new java.util.IdentityHashMap<>());
}
return _vmf_fToStringChecker;
}
private ThreadLocal> _vmf_fToStringChecker;
// end toString()
// ---------------------------- END TO_STRING ------------------------------
// --------------------------- BEGIN EQUALITY -----------------------------
@Override
public boolean equals(Object o) {
boolean entry = _vmf_getThreadLocalEquals().get().isEmpty();
try {
// test if the object pair (this,o) has been checked before
boolean isImmutable = (o instanceof eu.mihosoft.vmf.runtime.core.Immutable);
if (!isImmutable && _vmf_getThreadLocalEquals().get().containsKey(new EqualsPair(this, o))) {
// This pair has been seen before. That's why we return true now.
// If this pair wasn't equal, we wouldn't need to do a second
// comparison. Returning 'true' is equivalent to ignoring this
// particular test in the calling 'equals()' method.
return true;
} else {
if(!isImmutable) {
_vmf_getThreadLocalEquals().get().put(new EqualsPair(this, o), null);
}
entry = true;
}
if (o==null) return false;
else if (this==o) return true;
// if object is read-only wrapper then unwrap the actual object
if(o instanceof ReadOnlyGrammarModelImpl) {
o = ((ReadOnlyGrammarModelImpl)o)._vmf_getMutableObject();
}
// -- try our interface/implementation --
// perform the actual comparison for each property
if (o instanceof GrammarModelImpl) {
GrammarModelImpl other = (GrammarModelImpl) o;
return _vmf_equals(this.customRules, other.customRules) &&
_vmf_equals(this.grammarName, other.grammarName) &&
_vmf_equals(this.packageName, other.packageName) &&
_vmf_equals(this.ruleClasses, other.ruleClasses) &&
_vmf_equals(this.typeMappings, other.typeMappings);
}
// -- try every implementation that implements our interface --
// no implementation matched. we end the comparison.
return false;
} finally {
if (entry) {
_vmf_getThreadLocalEquals().get().clear();
_vmf_fEqualsChecker = null;
}
}
} // end equals()
private static boolean _vmf_equals(Object o1, Object o2) {
boolean oneAndOnlyOneIsNull = (o1 == null) != (o2 == null);
boolean collectionType = o1 instanceof VList || o2 instanceof VList;
// since we support lazy initialization for collections,
// uninitialized empty collection values are defined as equal to null
// otherwise we would have to initialize these values, which would then
// neutralize or even negate the positive effect of lazy initialization
if(oneAndOnlyOneIsNull && collectionType) {
if(o1==null) {
return ((VList)o2).isEmpty();
} else {
return ((VList)o1).isEmpty();
}
} else {
return Objects.equals(o1,o2);
}
}
@Override
public int hashCode() {
boolean entry = _vmf_getThreadLocalHashCode().get().isEmpty();
try {
// test if "this class" has been seen before
//
// WARNING we use `System.identityHashCode(this)` to prevent recursive
// hashCode() calls before we do the actual test. This would eliminate
// the effect of the thread-local map
if (_vmf_getThreadLocalHashCode().get().containsKey(System.identityHashCode(this))) {
return 0; // already visited
} else {
_vmf_getThreadLocalHashCode().get().put(System.identityHashCode(this), null);
int value = _vmf_deepHashCode(
this.customRules, this.grammarName, this.packageName, this.ruleClasses, this.typeMappings );
entry = true;
return value;
}
} finally {
if (entry) {
_vmf_getThreadLocalHashCode().get().clear();
_vmf_fHashCodeChecker = null;
}
}
} // end hashCode()
// fixes 'array discovery problems' with the 'Objects.hash(...)' method
// see http://stackoverflow.com/questions/30385018/how-to-use-java-7-objects-hash-with-arrays
private int _vmf_deepHashCode(Object... fields) {
// WARNING we are not allowed to pass arrays that contain itself
// or are contained in nested arrays
return Arrays.deepHashCode(fields);
} // end _vmf_deepHashCode()
/**
* The purpose of this class is to store a pair of objects used for equals().
* This class's equals() method checks equality by object identity. Same
* for hashCode() which uses identity hashes of 'first' and 'second' to
* compute the hash.
*
* This class can be used in conjunction with a regular HashMap to get
* similar results to an IdentityHashMap, except that in this case identity
* pairs can be used. And we don't have to use a map implementation that is
* deliberately broken by design.
*/
private static class EqualsPair {
final Object first;
final Object second;
public EqualsPair(Object first, Object second) {
this.first = first;
this.second = second;
}
@Override
public int hashCode() {
return Objects.hash(System.identityHashCode(first),
System.identityHashCode(second));
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final EqualsPair other = (EqualsPair) obj;
if (this.first!=other.first) {
return false;
}
if (this.second!=other.second) {
return false;
}
return true;
}
}
private ThreadLocal> _vmf_getThreadLocalEquals() {
if (_vmf_fEqualsChecker==null) {
_vmf_fEqualsChecker = ThreadLocal.withInitial(
() -> new java.util.HashMap<>());
}
return _vmf_fEqualsChecker;
}
private ThreadLocal> _vmf_getThreadLocalHashCode() {
if (_vmf_fHashCodeChecker==null) {
_vmf_fHashCodeChecker = ThreadLocal.withInitial(
() -> new java.util.HashMap<>());
}
return _vmf_fHashCodeChecker;
}
private ThreadLocal> _vmf_fEqualsChecker;
private ThreadLocal> _vmf_fHashCodeChecker;
// ---------------------------- END EQUALITY ------------------------------
// --------------------------- BEGIN CLONING -----------------------------
/**
* Package private copy constructor.
* It creates a deep or shallow copy of the specified other object.
* @param other other object
* @param deepCopy defines whether to perform a deep copy
*/
GrammarModelImpl (
GrammarModelImpl other,
boolean deepCopy, java.util.IdentityHashMap