
eu.mihosoft.vmf.vmftext.grammar.impl.AlternativeImpl 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.Alternative}.
*/
@SuppressWarnings("deprecation")
class AlternativeImpl implements Alternative, eu.mihosoft.vmf.runtime.core.internal.VObjectInternalModifiable, VCloneableInternal {
// --------------------------------------------------------------------
// --- declaration of member variables
// --------------------------------------------------------------------
/*package private*/ eu.mihosoft.vcollections.VList elements;
/*package private*/ int id;
/*package private*/ eu.mihosoft.vmf.vmftext.grammar.UPRuleBase parentRule;
/*package private*/ java.lang.String text;
// --------------------------------------------------------------------
// --- declaration of delegation variables
// --------------------------------------------------------------------
private eu.mihosoft.vmf.vmftext.grammar.unparser.SubRuleIndexUpdater _vmf_delegate0;
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 AlternativeImpl() {
// --------------------------------------------------------------------
// --- declaration of delegation methods
// --------------------------------------------------------------------
if(_vmf_delegate0==null) {
_vmf_delegate0 = new eu.mihosoft.vmf.vmftext.grammar.unparser.SubRuleIndexUpdater();
_vmf_delegate0.setCaller(this);
}
_vmf_delegate0.onAlternativeBaseInstantiated();
// --------------------------------------------------------------------
// --- 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
}
// --------------------------------------------------------------------
// --- public getter methods for accessing properties
// --------------------------------------------------------------------
@Override
public eu.mihosoft.vcollections.VList getElements() {
if(this.elements==null) {
this.elements = VList.newInstance(new java.util.ArrayList<>());
this.elements.addChangeListener((evt)-> {
evt.added().elements().forEach((e)->{
if(e==null) return;
// remove element from previous collection
// TODO use bulk operations if possible
if(e.getParentAlt()!=null) {
e.getParentAlt().getElements().remove(e);
}
try {
java.lang.reflect.Field field =
e.getClass().getDeclaredField("parentAlt");
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(
AlternativeImpl.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("parentAlt");
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(
AlternativeImpl.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.elements==null)
return this.elements;
// return VContainmentUtil.asContainmentList(this.elements, "parentAlt");
} // end of eu.mihosoft.vcollections.VList getElements()
@Override
public int getId() {
return this.id;
} // end of int getId()
@Override
public eu.mihosoft.vmf.vmftext.grammar.UPRuleBase getParentRule() {
return this.parentRule;
} // end of eu.mihosoft.vmf.vmftext.grammar.UPRuleBase getParentRule()
@Override
public java.lang.String getText() {
return this.text;
} // end of java.lang.String getText()
// --------------------------------------------------------------------
// --- public setter methods for setting properties
// --------------------------------------------------------------------
@Override
public void setId(int id) {
// return early if identical value has been set
if (this.id == id) {
return;
}
// set the new value
int oldValue = this.id;
this.id = id;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("id", oldValue, this.id);
} // setterDeclaration (setter method)
@Override
public void setParentRule(eu.mihosoft.vmf.vmftext.grammar.UPRuleBase parentRule) {
// opposite is a collection type. we just need to add this to the
// opposite collection which will handle the containment just fine
// nothing to do, values are identical
if(this.parentRule == parentRule) {
return;
}
// remove this from previous opposite
if(this.parentRule!=null) {
this.parentRule.getAlternatives().remove(this);
}
// add this to new opposite
if(parentRule!=null) {
parentRule.getAlternatives().add(this);
}
}
@Override
public void setText(java.lang.String text) {
// return early if identical value has been set
if (this.text == text) {
return;
}
// set the new value
java.lang.String oldValue = this.text;
this.text = text;
// fire property change event
_vmf_firePropertyChangeIfListenersArePresent("text", oldValue, this.text);
} // setterDeclaration (setter method)
// --------------------------------------------------------------------
// --- 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\":\"Alternative\"" +
", " + (this.elements==null?"[]":this.elements) +
", \"id\": \"" + this.id + "\"" +
", \"text\": \"" + this.text + "\"" +
"}";
} 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 ReadOnlyAlternativeImpl) {
o = ((ReadOnlyAlternativeImpl)o)._vmf_getMutableObject();
}
// -- try our interface/implementation --
// perform the actual comparison for each property
if (o instanceof AlternativeImpl) {
AlternativeImpl other = (AlternativeImpl) o;
return _vmf_equals(this.elements, other.elements) &&
_vmf_equals(this.id, other.id) &&
_vmf_equals(this.text, other.text);
}
// -- try every implementation that implements our interface --
// perform the actual comparison for each property
if (o instanceof LabeledAlternativeImpl) {
LabeledAlternativeImpl other = (LabeledAlternativeImpl) o;
return _vmf_equals(this.elements, other.elements) &&
_vmf_equals(this.id, other.id) &&
_vmf_equals(this.text, other.text);
}
// 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.elements, this.id, this.text );
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
*/
AlternativeImpl (
AlternativeImpl other,
boolean deepCopy, java.util.IdentityHashMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy