Sample

Schnappinger/argoUML/org.argouml.model.mdr.ModelEventPumpMDRImpl

Human rating uses the original dataset scale.

Human3.78
Lines1124

Scores

MethodScoreScaleDataset rank
(0–100)
Posnett00–1; higher is more readable
4.5Δ 39.0
Scalabrino0.6490–1; higher is more readable
29.1Δ 14.5
Dorn00–1; higher is more readable
3.3Δ 40.3
Mi0.5920–1; higher is more readable
59.7Δ 16.2
LLOC364Logical lines; fewer is more readable
6.3Δ 37.3
DeepSeek V4 Pro12.330–20; mean of three runs
17.2Δ 26.4
GPT-6.1 Sol130–20; mean of three runs
9.4Δ 34.2
BTTF0.3050–1; higher is more readable
21.1Δ 22.4
Human3.781–4; original rating
43.6

Views

Source code

/* $Id: ModelEventPumpMDRImpl.java 19907 2012-12-30 13:06:01Z closettop_nightlybuild $ ***************************************************************************** * Copyright (c) 2005-2012 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tom Morris ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 2005-2008 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.model.mdr; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.jmi.model.Association; import javax.jmi.model.AssociationEnd; import javax.jmi.model.Attribute; import javax.jmi.model.GeneralizableElement; import javax.jmi.model.ModelElement; import javax.jmi.model.ModelPackage; import javax.jmi.model.MofClass; import javax.jmi.model.NameNotFoundException; import javax.jmi.model.Reference; import javax.jmi.reflect.InvalidObjectException; import javax.jmi.reflect.RefAssociation; import javax.jmi.reflect.RefBaseObject; import javax.jmi.reflect.RefObject; import org.argouml.model.AbstractModelEventPump; import org.argouml.model.AddAssociationEvent; import org.argouml.model.AttributeChangeEvent; import org.argouml.model.DeleteInstanceEvent; import org.argouml.model.InvalidElementException; import org.argouml.model.Model; import org.argouml.model.NotImplementedException; import org.argouml.model.RemoveAssociationEvent; import org.argouml.model.UmlChangeEvent; import org.argouml.model.UmlChangeListener; import org.netbeans.api.mdr.MDRManager; import org.netbeans.api.mdr.MDRepository; import org.netbeans.api.mdr.events.AssociationEvent; import org.netbeans.api.mdr.events.AttributeEvent; import org.netbeans.api.mdr.events.InstanceEvent; import org.netbeans.api.mdr.events.MDRChangeEvent; import org.netbeans.api.mdr.events.MDRPreChangeListener; import org.netbeans.api.mdr.events.TransactionEvent; import org.netbeans.api.mdr.events.VetoChangeException; /** * The ModelEventPump for the MDR implementation.<p> * * This implements three different event dispatching interfaces * which support a variety of different types of listener registration. * We keep a single event listener registered with the repository * for all events and then re-dispatch events to those listeners * who have requested them.<p> * * @since ARGO0.19.5 * @author Ludovic Ma&icirc;tre * @author Tom Morris */ class ModelEventPumpMDRImpl extends AbstractModelEventPump implements MDRPreChangeListener { /** * Logger. */ private static final Logger LOG = Logger.getLogger(ModelEventPumpMDRImpl.class.getName()); private static final boolean VETO_READONLY_CHANGES = true; private MDRModelImplementation modelImpl; private Object registrationMutex = new Byte[0]; private MDRepository repository; private Boolean eventCountMutex = new Boolean(false); private int pendingEvents = 0; private Thread eventThread; /** * Map of Element/attribute tuples and the listeners they have registered. */ private Registry<PropertyChangeListener> elements = new Registry<PropertyChangeListener>(); /** * Map of Class/attribute tuples and the listeners they have registered. */ private Registry<PropertyChangeListener> listenedClasses = new Registry<PropertyChangeListener>(); /** * Map of subtypes for all types in our metamodel. */ private Map<String, Collection<String>> subtypeMap; /** * Map of all valid property names (association end names & attribute names) * for each class. */ private Map<String, Collection<String>> propertyNameMap; /** * Constructor. * * @param implementation The implementation. */ public ModelEventPumpMDRImpl(MDRModelImplementation implementation) { this(implementation, MDRManager.getDefault().getDefaultRepository()); } /** * Constructor. * * @param implementation The implementation. * @param repo The repository. */ public ModelEventPumpMDRImpl(MDRModelImplementation implementation, MDRepository repo) { super(); modelImpl = implementation; repository = repo; subtypeMap = buildTypeMap(modelImpl.getModelPackage()); propertyNameMap = buildPropertyNameMap(modelImpl.getModelPackage()); } /* * @see org.argouml.model.AbstractModelEventPump#addModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void addModelEventListener(PropertyChangeListener listener, Object modelElement, String[] propertyNames) { if (listener == null) { throw new IllegalArgumentException("A listener must be supplied"); } if (modelElement == null) { throw new IllegalArgumentException( "A model element must be supplied"); } registerModelEvent(listener, modelElement, propertyNames); } /* * @see org.argouml.model.AbstractModelEventPump#addModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void addModelEventListener(UmlChangeListener listener, Object modelElement, String[] propertyNames) { throw new NotImplementedException(); } /* * @see org.argouml.model.AbstractModelEventPump#addModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object) */ public void addModelEventListener(PropertyChangeListener listener, Object modelElement) { if (listener == null) { throw new IllegalArgumentException("A listener must be supplied"); } if (modelElement == null) { throw new IllegalArgumentException( "A model element must be supplied"); } registerModelEvent(listener, modelElement, null); } /* * @see org.argouml.model.AbstractModelEventPump#removeModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void removeModelEventListener(PropertyChangeListener listener, Object modelelement, String[] propertyNames) { unregisterModelEvent(listener, modelelement, propertyNames); } /* * @see org.argouml.model.AbstractModelEventPump#removeModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void removeModelEventListener(UmlChangeListener listener, Object modelelement, String[] propertyNames) { throw new NotImplementedException(); } /* * @see org.argouml.model.AbstractModelEventPump#removeModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object) */ public void removeModelEventListener(PropertyChangeListener listener, Object modelelement) { unregisterModelEvent(listener, modelelement, null); } /* * @see org.argouml.model.AbstractModelEventPump#addClassModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void addClassModelEventListener(PropertyChangeListener listener, Object modelClass, String[] propertyNames) { registerClassEvent(listener, modelClass, propertyNames); } /* * @see org.argouml.model.AbstractModelEventPump#removeClassModelEventListener(java.beans.PropertyChangeListener, * java.lang.Object, java.lang.String[]) */ public void removeClassModelEventListener(PropertyChangeListener listener, Object modelClass, String[] propertyNames) { unregisterClassEvent(listener, modelClass, propertyNames); } /** * Detect a change event in MDR and convert this to a change event from the * model interface. We also keep track of the number of pending changes so * that we can implement a simple flush interface.<p> * * The conversions are according to this table. * <pre> * MDR Event MDR Event Type Propogated Event * * InstanceEvent EVENT_INSTANCE_DELETE DeleteInstanceEvent * AttributeEvent EVENT_ATTRIBUTE_SET AttributeChangeEvent * AssociationEvent EVENT_ASSOCIATION_ADD AddAssociationEvent * AssociationEvent EVENT_ASSOCIATION_REMOVE RemoveAssociationEvent * </pre> * Any other events are ignored and not propogated beyond the model * subsystem. * * @param mdrEvent Change event from MDR * @see org.netbeans.api.mdr.events.MDRChangeListener#change */ public void change(MDRChangeEvent mdrEvent) { if (eventThread == null) { eventThread = Thread.currentThread(); } // TODO: This should be done after all events are delivered, but leave // it here for now to avoid last minute synchronization problems decrementEvents(); // Quick exit if it's a transaction event // (we get a lot of them and they are all ignored) if (mdrEvent instanceof TransactionEvent) { return; } List<UmlChangeEvent> events = new ArrayList<UmlChangeEvent>(); if (mdrEvent instanceof AttributeEvent) { AttributeEvent ae = (AttributeEvent) mdrEvent; events.add(new AttributeChangeEvent(ae.getSource(), ae.getAttributeName(), ae.getOldElement(), ae.getNewElement(), mdrEvent)); } else if (mdrEvent instanceof InstanceEvent && mdrEvent.isOfType(InstanceEvent.EVENT_INSTANCE_DELETE)) { InstanceEvent ie = (InstanceEvent) mdrEvent; events.add(new DeleteInstanceEvent(ie.getSource(), "remove", null, null, mdrEvent)); // Clean up index entries String mofid = ((InstanceEvent)mdrEvent).getInstance().refMofId(); modelImpl.removeElement(mofid); } else if (mdrEvent instanceof AssociationEvent) { AssociationEvent ae = (AssociationEvent) mdrEvent; if (ae.isOfType(AssociationEvent.EVENT_ASSOCIATION_ADD)) { events.add(new AddAssociationEvent( ae.getNewElement(), mapPropertyName(ae.getEndName()), ae.getOldElement(), // will always be null ae.getFixedElement(), ae.getFixedElement(), mdrEvent)); // Create a change event for the corresponding property events.add(new AttributeChangeEvent( ae.getNewElement(), mapPropertyName(ae.getEndName()), ae.getOldElement(), // will always be null ae.getFixedElement(), mdrEvent)); // Create an event for the other end of the association events.add(new AddAssociationEvent( ae.getFixedElement(), otherAssocEnd(ae), ae.getOldElement(), // will always be null ae.getNewElement(), ae.getNewElement(), mdrEvent)); // and a change event for that end events.add(new AttributeChangeEvent( ae.getFixedElement(), otherAssocEnd(ae), ae.getOldElement(), // will always be null ae.getNewElement(), mdrEvent)); } else if (ae.isOfType(AssociationEvent.EVENT_ASSOCIATION_REMOVE)) { events.add(new RemoveAssociationEvent( ae.getOldElement(), mapPropertyName(ae.getEndName()), ae.getFixedElement(), ae.getNewElement(), // will always be null ae.getFixedElement(), mdrEvent)); // Create a change event for the associated property events.add(new AttributeChangeEvent( ae.getOldElement(), mapPropertyName(ae.getEndName()), ae.getFixedElement(), ae.getNewElement(), // will always be null mdrEvent)); // Create an event for the other end of the association events.add(new RemoveAssociationEvent( ae.getFixedElement(), otherAssocEnd(ae), ae.getOldElement(), ae.getNewElement(), // will always be null ae.getOldElement(), mdrEvent)); // Create a change event for the associated property events.add(new AttributeChangeEvent( ae.getFixedElement(), otherAssocEnd(ae), ae.getOldElement(), ae.getNewElement(), // will always be null mdrEvent)); } else if (ae.isOfType(AssociationEvent.EVENT_ASSOCIATION_SET)) { LOG.log(Level.SEVERE, "Unexpected EVENT_ASSOCIATION_SET received"); } else { LOG.log(Level.SEVERE, "Unknown association event type " + ae.getType()); } } else { String name = mdrEvent.getClass().getName(); // Cut down on debugging noise if (!name.endsWith("CreateInstanceEvent")) { LOG.log(Level.FINE, "Ignoring MDR event " + mdrEvent); } } for (UmlChangeEvent event : events) { fire(event); // Unregister deleted instances after all events have been delivered if (event instanceof DeleteInstanceEvent) { elements.unregister(null, ((RefBaseObject) event.getSource()) .refMofId(), null); } } } private boolean isReadOnly(RefBaseObject object) { return modelImpl.isReadOnly(object.refOutermostPackage()); } /** * @param e Event from MDR indicating a planned change. * @see org.netbeans.api.mdr.events.MDRPreChangeListener#plannedChange */ public void plannedChange(MDRChangeEvent e) { if (VETO_READONLY_CHANGES) { if (e instanceof InstanceEvent) { if (e.isOfType(InstanceEvent.EVENT_INSTANCE_CREATE)) { RefBaseObject element = (RefBaseObject) ((InstanceEvent) e) .getSource(); if (isReadOnly(element)) { throw new VetoChangeException(e.getSource(), null); } } else { RefObject element = ((InstanceEvent) e).getInstance(); if (isReadOnly(element)) { throw new VetoChangeException(e.getSource(), element); } } } else if (e instanceof AssociationEvent) { // TODO: association changes are too hard to check easily // we don't know which end of the association is significant // without checking the metamodel for navigability of the ends // RefObject element = ((AssociationEvent) e).getFixedElement(); // if (isReadOnly(element)) { // throw new VetoChangeException(element, element); // } } else if (e instanceof AttributeEvent) { RefObject element = (RefObject) ((AttributeEvent) e) .getSource(); if (isReadOnly(element)) { throw new VetoChangeException(element, element); } } } synchronized (eventCountMutex) { pendingEvents++; } // Prototypical logging code that can be enabled and modified to // discover who's creating certain types of events // if (/* LOG.isDebugEnabled() && */ e instanceof AssociationEvent) { // AssociationEvent ae = (AssociationEvent) e; // if (ae.isOfType(AssociationEvent.EVENT_ASSOCIATION_REMOVE) // && "namespace".equals(ae.getEndName()) // /* && ae.getFixedElement() instanceof UmlPackage */) { // LOG.log(Level.FINE, "Removing element " + ae.getOldElement() // + " from package " + ae.getFixedElement()); // } // } } /** * @param e * MDR event which was announced to plannedChange then * subsequently cancelled. * @see org.netbeans.api.mdr.events.MDRPreChangeListener#changeCancelled */ public void changeCancelled(MDRChangeEvent e) { decrementEvents(); } /** * Decrement count of outstanding events and wake * any waiters when it becomes zero. */ private void decrementEvents() { synchronized (eventCountMutex) { pendingEvents--; if (pendingEvents <= 0) { eventCountMutex.notifyAll(); } } } /** * Fire an event to any registered listeners. */ private void fire(UmlChangeEvent event) { String mofId = ((RefBaseObject) event.getSource()).refMofId(); String className = getClassName(event.getSource()); // Any given listener is only called once even if it is // registered for multiple relevant matches Set<PropertyChangeListener> listeners = new HashSet<PropertyChangeListener>(); synchronized (registrationMutex) { listeners.addAll(elements.getMatches(mofId, event .getPropertyName())); // This will include all subtypes registered listeners.addAll(listenedClasses.getMatches(className, event .getPropertyName())); } if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Firing " + modelImpl.getMetaTypes().getName(event) + " source " + modelImpl.getMetaTypes().getName( event.getSource()) + " [" + ((RefBaseObject) event.getSource()).refMofId() + "]." + event.getPropertyName() + "," + formatElement(event.getOldValue()) + "->" + formatElement(event.getNewValue())); } if (!listeners.isEmpty()) { for (PropertyChangeListener pcl : listeners) { if (false /*(LOG.isDebugEnabled()*/) { LOG.log(Level.FINE, "Firing event on " + pcl.getClass().getName() + "[" + pcl + "]"); } pcl.propertyChange(event); } } else { // For debugging you probably want either this // OR the logging for every event which is fired - not both if (false/*LOG.isDebugEnabled()*/) { LOG.log(Level.FINE, "No listener for " + modelImpl.getMetaTypes().getName(event) + " source " + modelImpl.getMetaTypes().getName( event.getSource()) + " [" + ((RefBaseObject) event.getSource()).refMofId() + "]." + event.getPropertyName() + "," + event.getOldValue() + "->" + event.getNewValue()); } } } /** * Register a listener for a Model Event. The ModelElement's * MofID is used as the string to match against. */ private void registerModelEvent(PropertyChangeListener listener, Object modelElement, String[] propertyNames) { if (listener == null || modelElement == null) { throw new IllegalArgumentException("Neither listener (" + listener + ") or modelElement (" + modelElement + ") can be null! [Property names: " + propertyNames + "]"); } // Fetch the key before going in synchronized mode String mofId = ((RefBaseObject) modelElement).refMofId(); try { verifyAttributeNames(((RefBaseObject) modelElement).refMetaObject(), propertyNames); } catch (InvalidObjectException e) { throw new InvalidElementException(e); } if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Register [" + " element:" + formatElement(modelElement) + ", properties:" + formatArray(propertyNames) + ", listener:" + listener + "]"); } synchronized (registrationMutex) { elements.register(listener, mofId, propertyNames); } } /** * Unregister a listener for a Model Event. */ private void unregisterModelEvent(PropertyChangeListener listener, Object modelElement, String[] propertyNames) { if (listener == null || modelElement == null) { LOG.log(Level.SEVERE, "Attempt to unregister null listener(" + listener + ") or modelElement (" + modelElement + ")! [Property names: " + propertyNames + "]"); return; } if (!(modelElement instanceof RefBaseObject)) { LOG.log(Level.SEVERE, "Ignoring non-RefBaseObject received by " + "unregisterModelEvent - " + modelElement); return; } String mofId = ((RefBaseObject) modelElement).refMofId(); if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Unregister [" + " element:" + formatElement(modelElement) + ", properties:" + formatArray(propertyNames) + ", listener:" + listener + "]"); } synchronized (registrationMutex) { elements.unregister(listener, mofId, propertyNames); } } /** * Register a listener for metamodel Class (and all its * subclasses), optionally qualified by a list of * property names. * * TODO: verify that property/event names are legal for * this class in the metamodel */ private void registerClassEvent(PropertyChangeListener listener, Object modelClass, String[] propertyNames) { if (modelClass instanceof Class) { String className = getClassName(modelClass); if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Register class [" + modelImpl.getMetaTypes().getName(modelClass) + "properties:" + formatArray(propertyNames) + ", listener:" + listener + "]"); } Collection<String> subtypes = subtypeMap.get(className); verifyAttributeNames(className, propertyNames); synchronized (registrationMutex) { listenedClasses.register(listener, className, propertyNames); for (String subtype : subtypes) { listenedClasses.register(listener, subtype, propertyNames); } } return; } throw new IllegalArgumentException( "Don't know how to register class event for object " + modelClass); } /** * Unregister a listener for a class and its subclasses. */ private void unregisterClassEvent(PropertyChangeListener listener, Object modelClass, String[] propertyNames) { if (modelClass instanceof Class) { String className = getClassName(modelClass); if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Unregister class [" + className + ", properties:" + formatArray(propertyNames) + ", listener:" + listener + "]"); } Collection<String> subtypes = subtypeMap.get(className); synchronized (registrationMutex) { listenedClasses.unregister(listener, className, propertyNames); for (String subtype : subtypes) { listenedClasses.unregister(listener, subtype, propertyNames); } } return; } throw new IllegalArgumentException( "Don't know how to unregister class event for object " + modelClass); } private String getClassName(Object elementOrClass) { return modelImpl.getMetaTypes().getName(elementOrClass); } /* * @see org.argouml.model.ModelEventPump#startPumpingEvents() */ public void startPumpingEvents() { LOG.log(Level.FINE, "Start pumping events"); repository.addListener(this); } /* * @see org.argouml.model.ModelEventPump#stopPumpingEvents() */ public void stopPumpingEvents() { LOG.log(Level.FINE, "Stop pumping events"); repository.removeListener(this); } /* * @see org.argouml.model.ModelEventPump#flushModelEvents() */ public void flushModelEvents() { while (true) { synchronized (eventCountMutex) { if (pendingEvents <= 0 // Don't wait on ourselves, we'll deadlock! // TODO: We might want to throw an exception here || Thread.currentThread().equals(eventThread)) { return; } try { eventCountMutex.wait(); } catch (InterruptedException e) { LOG.log(Level.SEVERE, "Interrupted while waiting in flushModelEvents"); } } } } /** * Get name of opposite end of association using * reflection on metamodel. */ private String otherAssocEnd(AssociationEvent ae) { RefAssociation ra = (RefAssociation) ae.getSource(); Association a = (Association) ra.refMetaObject(); AssociationEnd aend = null; try { aend = (AssociationEnd) a.lookupElementExtended(ae.getEndName()); } catch (NameNotFoundException e) { LOG.log(Level.SEVERE, "Failed to find other end of association : " + ae.getSource() + " -> " + ae.getEndName()); return null; } return aend.otherEnd().getName(); } /** * Map from UML 1.4 names to UML 1.3 names * expected by ArgoUML.<p> * * Note: It would have less performance impact to do the * mapping during listener registration, but ArgoUML * depends on the value in the event. */ private static String mapPropertyName(String name) { // TODO: We don't want to do this once we have dropped UML1.3 // Map UML 1.4 names to UML 1.3 equivalents if ("typedParameter".equals(name)) { return "parameter"; } if ("typedFeature".equals(name)) { return "feature"; } return name; } /** * Formatters for debug output. */ private String formatArray(String[] array) { if (array == null) { return null; } String result = "["; for (int i = 0; i < array.length; i++) { result = result + array[i] + ", "; } return result.substring(0, result.length() - 2) + "]"; } private String formatElement(Object element) { try { if (element instanceof RefBaseObject) { return modelImpl.getMetaTypes().getName(element) + "<" + ((RefBaseObject) element).refMofId() + ">"; } else if (element != null) { return element.toString(); } } catch (InvalidObjectException e) { return modelImpl.getMetaTypes().getName(element) + "<deleted>"; } return null; } /** * Traverse metamodel and build list of subtypes for every metatype. */ private Map<String, Collection<String>> buildTypeMap(ModelPackage extent) { Map<String, Collection<String>> names = new HashMap<String, Collection<String>>(); for (Object metaclass : extent.getMofClass().refAllOfClass()) { ModelElement element = (ModelElement) metaclass; String name = element.getName(); if (names.containsKey(name)) { LOG.log(Level.SEVERE, "Found duplicate class '" + name + "' in metamodel"); } else { names.put(name, getSubtypes(extent, element)); // LOG.log(Level.FINE, " Class " + name + " has subtypes : " // + names.get(name)); } } return names; } /** * Recursive method to get all subtypes. * * TODO: Does this have a scalability problem? */ private Collection<String> getSubtypes(ModelPackage extent, ModelElement me) { Collection<String> allSubtypes = new HashSet<String>(); if (me instanceof GeneralizableElement) { GeneralizableElement ge = (GeneralizableElement) me; Collection<ModelElement> subtypes = extent.getGeneralizes() .getSubtype(ge); for (ModelElement st : subtypes) { allSubtypes.add(st.getName()); allSubtypes.addAll(getSubtypes(extent, st)); } } return allSubtypes; } /** * Traverse metamodel and build list of names for all attributes and * reference ends. */ private Map<String, Collection<String>> buildPropertyNameMap( ModelPackage extent) { Map<String, Collection<String>> names = new HashMap<String, Collection<String>>(); for (Reference reference : (Collection<Reference>) extent .getReference().refAllOfClass()) { mapAssociationEnd(names, reference.getExposedEnd()); mapAssociationEnd(names, reference.getReferencedEnd()); } for (Attribute attribute : (Collection<Attribute>) extent .getAttribute().refAllOfClass()) { mapPropertyName(names, attribute.getContainer(), attribute.getName()); } return names; } private void mapAssociationEnd(Map<String, Collection<String>> names, AssociationEnd end) { ModelElement type = end.otherEnd().getType(); mapPropertyName(names, type, end.getName()); } private boolean mapPropertyName(Map<String, Collection<String>> names, ModelElement type, String propertyName) { String typeName = type.getName(); boolean added = mapPropertyName(names, typeName, propertyName); Collection<String> subtypes = subtypeMap.get(typeName); if (subtypes != null) { for (String subtype : subtypes) { added &= mapPropertyName(names, subtype, propertyName); } } return added; } private boolean mapPropertyName(Map<String, Collection<String>> names, String typeName, String propertyName) { if (!names.containsKey(typeName)) { names.put(typeName, new HashSet<String>()); } boolean added = names.get(typeName).add(propertyName); if (!added) { // Because we map both ends of an association we'll see many // names twice // LOG.log(Level.FINE, "Duplicate property name found - {0}:{1}", new Object[]{typeName, propertyName}); } else { LOG.log(Level.FINE, "Added property name - {0}:{1}", new Object[]{typeName, propertyName}); } return added; } /** * Check whether given attribute names exist for this * metatype in the metamodel. Throw exception if not found. */ private void verifyAttributeNames(String className, String[] attributes) { // convert classname to RefObject RefObject ro = null; verifyAttributeNames(ro, attributes); } /** * Check whether given attribute names exist for this * metatype in the metamodel. Throw exception if not found. */ private void verifyAttributeNames(RefObject metaobject, String[] attributes) { // Only do verification if debug level logging is on // TODO: Should we leave this on always? - tfm if (LOG.isLoggable(Level.FINE)) { if (metaobject == null || attributes == null) { return; } // If we don't have a MofClass, see if we can get one from the // instance if (!(metaobject instanceof MofClass)) { metaobject = metaobject.refMetaObject(); } // If we still don't have a MofClass, something's wrong if (!(metaobject instanceof MofClass)) { throw new IllegalArgumentException( "Argument must be MofClass or instance of MofClass"); } MofClass metaclass = (MofClass) metaobject; Collection<String> names = propertyNameMap.get(metaclass.getName()); if (names == null) { names = Collections.emptySet(); } for (String attribute : attributes) { if (!names.contains(attribute) && !"remove".equals(attribute)) { // TODO: We also have code registering for the names of // a tagged value like "derived" LOG.log(Level.SEVERE, "Property '" + attribute + "' for class '" + metaclass.getName() + "' doesn't exist in metamodel"); // throw new IllegalArgumentException("Property '" // + attribute + "' doesn't exist in metamodel"); } } } } @SuppressWarnings("unchecked") public List getDebugInfo() { List info = new ArrayList(); info.add("Event Listeners"); for (Iterator it = elements.registry.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = (Map.Entry) it.next(); String item = entry.getKey().toString(); List modelElementNode = newDebugNode(getDebugDescription(item)); info.add(modelElementNode); Map propertyMap = (Map) entry.getValue(); for (Iterator propertyIterator = propertyMap.entrySet().iterator(); propertyIterator.hasNext();) { Map.Entry propertyEntry = (Map.Entry) propertyIterator.next(); List propertyNode = newDebugNode(propertyEntry.getKey().toString()); modelElementNode.add(propertyNode); List listenerList = (List) propertyEntry.getValue(); for (Iterator listIt = listenerList.iterator(); listIt.hasNext(); ) { Object listener = listIt.next(); List listenerNode = newDebugNode( listener.getClass().getName()); propertyNode.add(listenerNode); } } } return info; } private List<String> newDebugNode(String name) { List<String> list = new ArrayList<String>(); list.add(name); return list; } private String getDebugDescription(String mofId) { Object modelElement = repository.getByMofId(mofId); String name = Model.getFacade().getName(modelElement); if (name != null && name.trim().length() != 0) { return "\"" + name + "\" - " + modelElement.toString(); } else { return modelElement.toString(); } } } /** * A simple typed registry which supports two levels of string keys. * * @param <T> type of object to be registered * @author Tom Morris */ class Registry<T> { private static final Logger LOG = Logger.getLogger(Registry.class.getName()); Map<String, Map<String, List<T>>> registry; /** * Construct a new registry for the given type of object. */ Registry() { registry = Collections .synchronizedMap(new HashMap<String, Map<String, List<T>>>()); } /** * Register an object with given keys(s) in the registry. The object is * registered in multiple locations for quick lookup. During matching an * object registered without subkeys will match any subkey. Multiple calls * with the same item and key pair will only result in a single registration * being made. * * @param item object to be registered * @param key primary key for registration * @param subkeys array of subkeys. If null, register under primary key * only. The special value of the empty string ("") must not * be used as a subkey by the caller. */ void register(T item, String key, String[] subkeys) { // Lookup primary key, creating new entry if needed Map<String, List<T>> entry = registry.get(key); if (entry == null) { entry = new HashMap<String, List<T>>(); registry.put(key, entry); } // If there are no subkeys, register using our special value // to indicate that this is a primary key only registration if (subkeys == null || subkeys.length < 1) { subkeys = new String[] { "", }; } for (int i = 0; i < subkeys.length; i++) { List<T> list = entry.get(subkeys[i]); if (list == null) { list = new ArrayList<T>(); entry.put(subkeys[i], list); } if (!list.contains(item)) { list.add(item); } else { LOG.log(Level.FINE, "Duplicate registration attempt for {0}: {1} Listener: {2}", new Object[]{key,subkeys,item}); } } } /** * Unregister an item or all items which match key set. * * @param item object to be unregistered. If null, unregister all * matching objects. * @param key primary key for registration * @param subkeys array of subkeys. If null, unregister under primary * key only. */ void unregister(T item, String key, String[] subkeys) { Map<String, List<T>> entry = registry.get(key); if (entry == null) { return; } if (subkeys != null && subkeys.length > 0) { for (int i = 0; i < subkeys.length; i++) { lookupRemoveItem(entry, subkeys[i], item); } } else { if (item == null) { registry.remove(key); } else { lookupRemoveItem(entry, "", item); } } } private void lookupRemoveItem(Map<String, List<T>> map, String key, T item) { List<T> list = map.get(key); if (list == null) { return; } if (item == null) { map.remove(key); return; } if (LOG.isLoggable(Level.FINE)) { if (!list.contains(item)) { LOG.log(Level.FINE, "Attempt to unregister non-existant registration {0} Listener: {1}", new Object[]{key, item}); } } while (list.contains(item)) { list.remove(item); } if (list.isEmpty()) { map.remove(key); } } /** * Return a list of items which have been registered for given key(s). * Returns items registered both for the key/subkey pair as well as * those registered just for the primary key. * @param key * @param subkey * @return collection of items previously registered. */ Collection<T> getMatches(String key, String subkey) { List<T> results = new ArrayList<T>(); Map<String, List<T>> entry = registry.get(key); if (entry != null) { if (entry.containsKey(subkey)) { results.addAll(entry.get(subkey)); } if (entry.containsKey("")) { results.addAll(entry.get("")); } } return results; } }

Loading feature diagnosis…

Source regions

Feature contributions

Feature contributions

FeatureValueContribution
(log-odds)
Halstead volumeHow much symbolic information the code contains. It combines the total number of operators and values with the number of distinct ones.41862.689−1381.469
Source linesThe number of non-empty source lines.1124+449.6
Byte entropyHow varied the bytes in the source text are. Repeated bytes give a lower value; a more even mixture gives a higher value.4.515−6.773

Intercept 8.87 + feature contributions = -929.771 (log-odds).
Score = sigmoid(-929.771) = 0.

Score0.649

Feature contributions

FeatureValueContribution
(log-odds)
Comment layout frequencyHow frequently the layout switches between lines containing only comments or whitespace and lines containing code.1125−38.797
Indentation frequencyHow rapidly indentation changes from line to line.853+28.767
Number layout frequencyHow rapidly the number of numeric values per line changes through the code.853−19.153
Identifier layout (vertical)How rapidly the visual pattern of identifier characters changes from top to bottom.1123.925+15.3
Identifier layout (horizontal)How rapidly the visual pattern of identifier characters changes from left to right. It measures spatial frequency, not the meaning of the names.118.977−1.374
Comment layout (horizontal)How rapidly the visual pattern of comment characters changes from left to right.118.878+1.114
Operator visual areaThe share of the code’s visual area occupied by operators such as +, =, and *.0.066+0.301

Intercept 0.07 + feature contributions = -13.771 (log-odds).
Score = sigmoid(-13.771) = 0.

Score0.592
Score364
Run 1

The code has a clear overall structure with logical separation of methods and a helper Registry class, but suffers from severe readability issues. The change() method is extremely long (~90 lines) and deeply nested (if/else if chains up to 4 levels), violating flat structure and action granularity principles. Many lines perform multiple actions, especially in event construction and logging statements. The intent is generally clear through comments and method names, but the sheer density of comments—including large blocks of commented-out debugging code—clutters the reading flow. Repeated null checks and validation logic add unnecessary verbosity. The Registry class, while well-documented, uses non-descriptive variable names like 'entry', 'list', and 'map'. The constructors and field organization are reasonable, but the class is bloated with utility methods (formatElement, formatArray, mapPropertyName) that could be moved to a dedicated utility. Overall, while functional and somewhat organized, the excessive nesting, long methods, and cluttered comments make this code difficult to parse quickly.

Run 2

The code is long and includes extensive licensing and comments, but its logical structure is generally clear. It is only moderately nested, with mostly flat if/else chains. Intent is often explicit via descriptive method names and logging, though the event dispatching logic in change() is dense and hard to follow. Code length is high due to repeated logging blocks, comments, and dual copyright headers; many commented-out debug sections remain. Action granularity is mostly one action per line, but some constructors and conditional blocks pack multiple steps. Reading flow is interrupted by large comment blocks, frequent logging, and commented-out code. The supporting Registry class is readable and well-documented. Overall, maintainability would benefit from removing dead comments, consolidating event conversion logic, and extracting helper methods for clarity.

Run 3

The code is generally well-structured and organized into logical methods, but suffers from readability issues. The main change() method is quite long and contains deeply nested conditionals (especially in the AssociationEvent handling), which makes the flow difficult to follow. The comments are helpful but sometimes excessive, and there is a lot of commented-out code that clutters the file. Method names are clear and each method generally has a single responsibility, though some methods like change() and getDebugInfo() have multiple levels of nesting and do too much. The use of magic strings like "" as a special value in the Registry class is not obvious without reading comments. The code makes good use of early returns in some methods, which improves flow, but the overall length of the file and the depth of nesting in event handling logic reduces readability. The reading flow is natural in most places but gets interrupted by long conditional chains and debug logging scattered throughout the main logic.

Run 1

1. Code Structure: Responsibilities are separated into registration, dispatch, metadata, and registry helpers, but the main class combines many concerns. 2. Nesting: Most helpers are straightforward; plannedChange, attribute verification, and debug traversal contain substantial nesting. 3. Clarity of intent: Descriptive method names and explanatory comments clarify event handling, but abbreviated variables, raw collections, empty-string registry sentinels, and unfinished verification obscure details. 4. Code length: Repeated association-event construction, duplicated validation, verbose logging, and commented-out debugging make the file unnecessarily long. 5. Action granularity: Statements generally perform one action per line, although multiline constructors and chained expressions require careful tracking of arguments. 6. Reading flow: Consistent formatting, guard clauses, and method grouping support navigation, while long dispatch branches, disabled code, TODOs, and repetitive boilerplate interrupt the flow. Overall, reasonably understandable but verbose and unevenly complex.

Run 2

Code Structure: Responsibilities are organized into named methods, with a separate Registry helper, but the event pump mixes conversion, registration, synchronization, validation, and debugging. Nesting: Most helpers are shallow, while plannedChange, verification, and debug traversal contain several nested levels. Clarity of intent: Descriptive method names, documentation, and the event-conversion table help; abbreviated variables, empty-string sentinel keys, disabled branches, and incomplete validation obscure some behavior. Code length: Repeated association-event construction, duplicated registration logic, verbose logging, and commented-out code make the implementation unnecessarily long. Action granularity: Statements generally perform one action per line, although lengthy constructor calls and chained expressions require careful tracking. Reading flow: Consistent indentation and logical method boundaries support navigation, but large methods, repetitive boilerplate, raw collections, and obsolete debugging fragments interrupt the flow.

Run 3

Code Structure: Responsibilities are separated into named methods and a reusable Registry class, but the event pump combines dispatch, registration, synchronization, metamodel traversal, and debugging. Nesting: Most helpers are shallow, while plannedChange, validation, and debug traversal contain several nested levels. Clarity of intent: Descriptive method names, documentation, and the event-conversion table help; placeholder validation, disabled logging, raw collections, and the empty-string registry sentinel obscure behavior. Code length: Repetitive association-event construction, duplicate validation, verbose logging, and commented-out code make the implementation unnecessarily long. Action granularity: Statements generally perform one action each, although event constructors have many positional arguments that require careful comparison. Reading flow: Consistent formatting and logical method boundaries support navigation, but long branching methods, repeated casts, legacy comments, and synchronization details interrupt the main flow.