/* * Copyright 2008 Lockheed Martin Corporation, except as stated in the file * entitled Licensing-Information. All modifications copyright 2009 Data Access Technologies, Inc. Licensed under the Academic Free License * version 3.0 (http://www.opensource.org/licenses/afl-3.0.php), except as stated * in the file entitled Licensing-Information. * * Contributors: * MDS - initial API and implementation * */ package org.modeldriven.fuml.assembly; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.modeldriven.fuml.FumlObject; import org.modeldriven.fuml.repository.Class_; import org.modeldriven.fuml.repository.Classifier; import org.modeldriven.fuml.repository.Repository; import org.modeldriven.fuml.xmi.AbstractXmiNodeVisitor; import org.modeldriven.fuml.xmi.XmiExternalReferenceElement; import org.modeldriven.fuml.xmi.XmiInternalReferenceElement; import org.modeldriven.fuml.xmi.XmiNode; import org.modeldriven.fuml.xmi.XmiNodeVisitor; import org.modeldriven.fuml.xmi.XmiNodeVisitorStatus; import org.modeldriven.fuml.xmi.stream.StreamNode; import org.modeldriven.fuml.xmi.validation.ErrorCode; import org.modeldriven.fuml.xmi.validation.ErrorSeverity; import org.modeldriven.fuml.xmi.validation.ValidationError; import fUML.Syntax.Classes.Kernel.Element; import fUML.Syntax.Classes.Kernel.PrimitiveType; public class ElementGraphAssembler extends AbstractXmiNodeVisitor implements XmiNodeVisitor, AssemblerResults { private static Log log = LogFactory.getLog(ElementGraphAssembler.class); private Repository metadata = Repository.INSTANCE; private Map<XmiNode, ElementAssembler> xmiNodeToAssemblerMap = new HashMap<XmiNode, ElementAssembler>(); private Map<String, ElementAssembler> resultsAssemblerMap = new HashMap<String, ElementAssembler>(); private List<ElementAssembler> roots = new ArrayList<ElementAssembler>(); private boolean assembleExternalReferences = true; private List<ElementAssemblerEventListener> eventListeners; @SuppressWarnings("unused") private ElementGraphAssembler() { } public ElementGraphAssembler(XmiNode xmiRoot, boolean assembleExternalReferences) { super(xmiRoot); this.assembleExternalReferences = assembleExternalReferences; } public ElementGraphAssembler(XmiNode xmiRoot) { this(xmiRoot, true); } public void start() { // create an assembler hierarchy super.xmiRoot.accept(this); for (ElementAssembler root : roots) root.acceptBreadthFirst(new ReferenceFeatureAssembler()); for (ElementAssembler root : roots) root.acceptBreadthFirst(new ElementLinker()); for (ElementAssembler root : roots) root.associateDeferredGeneralizations(); for (ElementAssembler root : roots) root.acceptBreadthFirst(new LibraryRegistration()); // TODO: move to // library package for (ElementAssembler root : roots) root.acceptBreadthFirst(new NotifyEventListeners()); if (eventListeners != null) for (ElementAssemblerEventListener listener : eventListeners) listener.elementGraphAssembled( new ElementAssemblerResultsEvent(this)); } public void clear() { this.xmiNodeToAssemblerMap.clear(); this.classifierMap.clear(); this.references.clear(); this.resultsAssemblerMap.clear(); this.roots.clear(); } public void addEventListener(ElementAssemblerEventListener eventListener) { if (eventListeners == null) eventListeners = new ArrayList<ElementAssemblerEventListener>(); this.eventListeners.add(eventListener); } public void removeEventListener(ElementAssemblerEventListener eventListener) { if (eventListeners == null) return; this.eventListeners.remove(eventListener); } public void visit(XmiNode target, XmiNode sourceXmiNode, String sourceKey, XmiNodeVisitorStatus status, int level) { // The XMI root is just packaging, not something we want to assemble, so for // models with one or more profiles applied, the XMI root has multiple // child nodes as direct descendants, creating effectively a set of graphs. We ignore // the XMI root here making it's direct descendants actual root assemblers. if (sourceXmiNode == null && "XMI".equals(target.getLocalName())) { if (log.isDebugEnabled()) log.debug("ignoring root XMI node"); return; } XmiNode source = sourceXmiNode; if (source != null && "XMI".equals(source.getLocalName())) { source = null; if (log.isDebugEnabled()) log.debug("ignoring source XMI node as parent"); } if (log.isDebugEnabled()) if (source != null) log.debug("visit: " + target.getLocalName() + " \t\tsource: " + source.getLocalName()); else log.debug("visit: " + target.getLocalName()); StreamNode eventNode = (StreamNode) target; Classifier classifier = this.findClassifier(target, source); if (classifier == null) classifier = findClassifierFromImportAdapter(target); if (classifier == null) { ValidationError error = new ValidationError(eventNode, ErrorCode.UNDEFINED_CLASS, ErrorSeverity.WARN); log.warn(error.toString()); String xmiType = target.getXmiType(); if (xmiType != null && xmiType.length() > 0) log.warn("ignoring element, " + xmiType); else log.warn("ignoring element, " + target.getLocalName()); return; } if (log.isDebugEnabled()) log.debug("identified element '" + target.getLocalName() + "' as classifier, " + classifier.getName()); classifierMap.put(target, classifier); // PrimitiveType elements have no attributes or content if (classifier.getDelegate() instanceof PrimitiveType) { return; } boolean hasAttributes = eventNode.hasAttributes(); if (isNotReferenceElement(target, classifier, hasAttributes)) return; // must be an attribute, handled in ElementAssembler ElementAssembler sourceAssembler = null; if (source != null) sourceAssembler = xmiNodeToAssemblerMap.get(source); // If the element is a "child" and represents just a reference, we don't // need an assembler // for it. Just add it as a reference to the parent for later lookup. if (sourceAssembler != null) { if (isInternalReferenceElement(eventNode, classifier, hasAttributes)) { sourceAssembler.addReference(new XmiInternalReferenceElement(eventNode, classifier)); return; } if (isExternalReferenceElement(eventNode, classifier, hasAttributes)) { sourceAssembler.addReference(new XmiExternalReferenceElement(eventNode, classifier)); return; } } ElementAssembler assembler = new ElementAssembler(target, source, (Class_)classifier, this.resultsAssemblerMap); assembler.setAssembleExternalReferences(this.assembleExternalReferences); assembler.assembleElementClass(); assembler.assembleFeatures(); this.resultsAssemblerMap.put(assembler.getXmiId(), assembler); this.xmiNodeToAssemblerMap.put(target, assembler); // build an assembler hierarchy if (sourceAssembler != null) { sourceAssembler.add(assembler); assembler.setParentAssembler(sourceAssembler); } if (source == null) { roots.add(assembler); } } public List<FumlObject> getResults() { List<FumlObject> results = new ArrayList<FumlObject>(); Iterator<String> keys = resultsAssemblerMap.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); ElementAssembler assembler = resultsAssemblerMap.get(key); results.add(assembler.getTargetObject()); } return results; } public List<String> getResultsXmiIds() { List<String> results = new ArrayList<String>(); Iterator<String> keys = resultsAssemblerMap.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); results.add(key); } return results; } public FumlObject lookupResult(String xmiId) { return resultsAssemblerMap.get(xmiId).getTargetObject(); } class ReferenceFeatureAssembler implements AssemblerVisitor { private Log log = LogFactory.getLog(ReferenceFeatureAssembler.class); public void begin(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { ElementAssembler targetAssembler = (ElementAssembler) target; ElementAssembler sourceAssembler = (ElementAssembler) source; if (log.isDebugEnabled()) if (source != null) log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName() + " \t\tsource: " + sourceAssembler.getTargetClass().getSimpleName()); else log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName()); if (log.isDebugEnabled()) log.debug("postassemble: (" + targetAssembler.getTargetClass().getSimpleName() + ")" + " " + targetAssembler.getPrototype().getName()); targetAssembler.assembleReferenceFeatures(); } public void end(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { } } class ElementLinker implements AssemblerVisitor { private Log log = LogFactory.getLog(ElementLinker.class); public void begin(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { ElementAssembler targetAssembler = (ElementAssembler) target; ElementAssembler sourceAssembler = (ElementAssembler) source; if (log.isDebugEnabled()) if (source != null) log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName() + " \t\tsource: " + sourceAssembler.getTargetClass().getSimpleName()); else log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName()); if (log.isDebugEnabled()) log.debug("postassemble: (" + targetAssembler.getTargetClass().getSimpleName() + ")" + " " + targetAssembler.getPrototype().getName()); if (sourceAssembler != null) targetAssembler.associateElement(sourceAssembler); } public void end(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { } } class LibraryRegistration implements AssemblerVisitor { private Log log = LogFactory.getLog(LibraryRegistration.class); public void begin(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { ElementAssembler targetAssembler = (ElementAssembler) target; ElementAssembler sourceAssembler = (ElementAssembler) source; if (log.isDebugEnabled()) if (source != null) log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName() + " \t\tsource: " + sourceAssembler.getTargetClass().getSimpleName()); else log.debug("begin: " + targetAssembler.getTargetClass().getSimpleName()); if (log.isDebugEnabled()) log.debug("postassemble: (" + targetAssembler.getTargetClass().getSimpleName() + ")" + " " + targetAssembler.getPrototype().getName()); targetAssembler.registerElement(); } public void end(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { } } class NotifyEventListeners implements AssemblerVisitor { private Log log = LogFactory.getLog(LibraryRegistration.class); public void begin(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { ElementAssembler targetAssembler = (ElementAssembler) target; if (eventListeners != null) for (ElementAssemblerEventListener listener : eventListeners) listener.elementAssembled( new ElementAssemblerEvent(targetAssembler.getTarget())); } public void end(AssemblerNode target, AssemblerNode source, String sourceKey, int level) { } } public boolean isAssembleExternalReferences() { return assembleExternalReferences; } public void setAssembleExternalReferences(boolean assembleExternalReferences) { this.assembleExternalReferences = assembleExternalReferences; } }