/* * Initial version copyright 2008 Lockheed Martin Corporation, except * as stated in the file entitled Licensing-Information. * * Modifications: * Copyright 2009 Data Access Technologies, Inc. * Copyright 2013 Ivar Jacobson International SA * * 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 * IJI * */ package org.modeldriven.fuml.repository.model; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.UnmarshalException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.modeldriven.fuml.FumlSystemProperty; import org.modeldriven.fuml.bind.DefaultValidationEventHandler; import org.modeldriven.fuml.common.reflect.ReflectionUtils; import org.modeldriven.fuml.config.ExtensionPackage; import org.modeldriven.fuml.config.FumlConfiguration; import org.modeldriven.fuml.repository.config.Artifact; import org.modeldriven.fuml.repository.config.IgnoredClass; import org.modeldriven.fuml.repository.config.IgnoredPackage; import org.modeldriven.fuml.repository.config.RepositoryConfig; import org.modeldriven.fuml.repository.config.RegisteredPackage; import org.modeldriven.fuml.repository.Class_; import org.modeldriven.fuml.repository.Classifier; import org.modeldriven.fuml.repository.Element; import org.modeldriven.fuml.repository.Package; import org.modeldriven.fuml.repository.Property; import org.modeldriven.fuml.repository.RepositoryConfigDataBinding; import org.modeldriven.fuml.repository.RepositoryMapping; import org.modeldriven.fuml.repository.RepositorylException; import org.modeldriven.fuml.repository.Extension; import org.modeldriven.fuml.repository.Stereotype; import org.modeldriven.fuml.repository.merge.PackageGraphNode; import org.modeldriven.fuml.repository.merge.PackageGraphVisitor; import org.modeldriven.fuml.xmi.InvalidReferenceException; import org.modeldriven.fuml.xmi.XmiException; import org.xml.sax.SAXException; import fUML.Syntax.Classes.Kernel.Generalization; import fUML.Syntax.Classes.Kernel.Operation; import fUML.Syntax.Classes.Kernel.PackageableElement; public class InMemoryRepository extends InMemoryMapping implements org.modeldriven.fuml.repository.Repository { private static Log log = LogFactory.getLog(InMemoryRepository.class); private static InMemoryRepository instance = null; private static String defaultConfigFileName = "RepositoryConfig.xml"; private static final List<Classifier> EMPTY_CLASSIFIER_LIST = new ArrayList<Classifier>(); private RepositoryConfig config; //private Map<String, Map<String, Property>> classNameToAttributeMap = new HashMap<String, Map<String, Property>>(); //protected Map<String, Map<String, Operation>> classNameToOperationMap = new HashMap<String, Map<String, Operation>>(); private Map<String, IgnoredPackage> ignoredPackageNameMap = new HashMap<String, IgnoredPackage>(); private Map<String, IgnoredClass> ignoredClassNameMap = new HashMap<String, IgnoredClass>(); private String activeConfigFileName; private InMemoryRepository() { log.info("initializing..."); try { RepositoryConfigDataBinding configBinding = new RepositoryConfigDataBinding( new DefaultValidationEventHandler()); activeConfigFileName = System.getProperty(FumlSystemProperty.REPOSITORY.getProperty(), defaultConfigFileName); config = unmarshalConfig(activeConfigFileName, configBinding); } catch (SAXException e) { throw new RepositorylException(e); } catch (JAXBException e) { throw new RepositorylException(e); } Iterator<IgnoredPackage> packages = config.getIgnoredPackage().iterator(); while (packages.hasNext()) { IgnoredPackage pkg = packages.next(); ignoredPackageNameMap.put(pkg.getName(), pkg); } Iterator<IgnoredClass> classes = config.getIgnoredClass().iterator(); while (classes.hasNext()) { IgnoredClass c = classes.next(); ignoredClassNameMap.put(c.getName(), c); } this.bootstrap(); this.construct(); } public static InMemoryRepository getInstance() throws RepositorylException { if (instance == null) initializeInstance(); return instance; } private static synchronized void initializeInstance() throws RepositorylException { if (instance == null) instance = new InMemoryRepository(); } private void construct() { log.info("cacheing classifier attributes and operations..."); Iterator<String> classes = classifierNameToClassifierMap.keySet().iterator(); while (classes.hasNext()) { String className = classes.next(); Classifier classifier = classifierNameToClassifierMap.get(className); if (classifier instanceof Class_) construct((Class_)classifier, className); } } /** * Collects attributes and operations for the given class and * maps it by default UML namespace URI and artifact namespace URI * qualified names. Because of the mapping to default UML namespace, * This method is only for use on initialization * when M2 level FUML/UML artifacts are being loaded. * @param clss the class * @param className the class name */ private void construct(Class_ clss, String className) { List<Property> attributes = new ArrayList<Property>(); collectAttributes(clss, attributes); //classNameToAttributeMap.put(className, attributes); List<Operation> operations = new ArrayList<Operation>(); collectOperations(clss, operations); //classNameToOperationMap.put(className, operations); org.modeldriven.fuml.repository.model.Class_ implClass = (org.modeldriven.fuml.repository.model.Class_)clss; implClass.setAttributes(attributes); implClass.setOperations(operations); // map final unqualifed class (post merge) to the default namespace, ... // FIXME: do we map by every "supported" namespace // FIXME: do we map by the final package name post merge? This currently // causes errors below. String namespaceQualifiedName = this.config.getDefaultUMLNamespaceURI() + "#" + className; this.qualifiedClassifierNameToClassifierMap.put(namespaceQualifiedName, clss); String artifactQualifiedName = clss.getArtifact().getNamespaceURI() + "#" + className; this.qualifiedClassifierNameToClassifierMap.put(artifactQualifiedName, clss); //String packageQualifiedName = implClass.getDelegate().package_.name + "." + className; //this.qualifiedClassifierNameToClassifierMap.put(packageQualifiedName, clss); } /** * Collects attributes and operations for the given class and * maps it by artifact(file) namespace URI qualified * names. * @param clss the class * @param className the class name */ public void loadClass(Class_ clss) { List<Property> attributes = new ArrayList<Property>(); collectAttributes(clss, attributes); List<Operation> operations = new ArrayList<Operation>(); collectOperations(clss, operations); org.modeldriven.fuml.repository.model.Class_ implClass = (org.modeldriven.fuml.repository.model.Class_)clss; implClass.setAttributes(attributes); implClass.setOperations(operations); String artifactQualifiedName = clss.getArtifact().getNamespaceURI() + "#" + clss.getName(); this.qualifiedClassifierNameToClassifierMap.put(artifactQualifiedName, clss); } private void bootstrap() { Map<Artifact, ModelAssembler> factoryMap = new HashMap<Artifact, ModelAssembler>(); Iterator<Artifact> artifacts = config.getArtifact().iterator(); while (artifacts.hasNext()) { Artifact artifact = artifacts.next(); Object[] args = { artifact, this, this }; Class[] types = { Artifact.class, RepositoryMapping.class, org.modeldriven.fuml.repository.Repository.class }; try { ModelAssembler factory = (ModelAssembler)ReflectionUtils.instanceForName( artifact.getFactoryClassName(), args, types); factoryMap.put(artifact, factory); } catch (Throwable e) { log.error(e.getMessage(), e); throw new RepositorylException(e); } } log.info("performing package merge..."); merge(); artifacts = config.getArtifact().iterator(); while (artifacts.hasNext()) { Artifact artifact = artifacts.next(); ModelAssembler factory = factoryMap.get(artifact); Iterator<RegisteredPackage> packages = artifact.getRegisteredPackage().iterator(); while (packages.hasNext()) { RegisteredPackage pkg = packages.next(); registerPackage((org.modeldriven.fuml.repository.RepositoryArtifact)factory, pkg.getName()); } } } private void merge() { List<PackageGraphNode> roots = findMergeRoots(); Iterator<PackageGraphNode> rootIter = roots.iterator(); while (rootIter.hasNext()) { PackageGraphNode root = rootIter.next(); Package rootPackage = (Package)this.getElementById(root.getId()); if (log.isDebugEnabled()) log.debug("merging package root: " + rootPackage.getHref()); PackageGraphVisitor visitor = new PackageGraphVisitor() { public void visit(PackageGraphNode target, PackageGraphNode source) { if (source != null) // it's not a root { if (log.isDebugEnabled()) log.debug("visit: " + target.getPackage().getQualifiedName() + "<-" + source.getPackage().getQualifiedName()); mergePackage(source.getPackage().getDelegate(), target.getPackage().getDelegate()); } } }; root.accept(visitor); } } private Package getPackage(org.modeldriven.fuml.repository.RepositoryArtifact artifact, String qualifiedName) { List<Package> artifactPackages = artifactURIToPackagesMap.get(artifact.getURN()); Iterator<Package> iter = artifactPackages.iterator(); while (iter.hasNext()) { Package p = iter.next(); if (qualifiedName.equals(p.getQualifiedName())) return p; } throw new RepositorylException("could not get package, " + qualifiedName); } private void registerPackage(org.modeldriven.fuml.repository.RepositoryArtifact aftifact, String qualifiedName) { Package p = getPackage(aftifact, qualifiedName); registerPackage(p); } private void registerPackage(Package p) { Iterator<PackageableElement> elementIter = p.getPackagedElement().iterator(); while (elementIter.hasNext()) { PackageableElement element = elementIter.next(); if (element instanceof fUML.Syntax.Classes.Kernel.Class_) { fUML.Syntax.Classes.Kernel.Class_ c = (fUML.Syntax.Classes.Kernel.Class_)element; Class_ clss = new org.modeldriven.fuml.repository.model.Class_(c, p.getArtifact()); classifierNameToClassifierMap.put(c.name, clss); classifierNameToPackageNameMap.put(c.name, p.getQualifiedName()); } else if (element instanceof fUML.Syntax.Classes.Kernel.Classifier) { fUML.Syntax.Classes.Kernel.Classifier c = (fUML.Syntax.Classes.Kernel.Classifier)element; Classifier classifier = new org.modeldriven.fuml.repository.model.Classifier(c, p.getArtifact()); classifierNameToClassifierMap.put(c.name, classifier); classifierNameToPackageNameMap.put(c.name, p.getQualifiedName()); } else if (element instanceof fUML.Syntax.Classes.Kernel.Package) { Package pkg = new org.modeldriven.fuml.repository.model.Package((fUML.Syntax.Classes.Kernel.Package)element, p.getArtifact()); registerPackage(pkg); } } } private List<PackageGraphNode> findMergeRoots() { Map<String, PackageGraphNode> sources = new HashMap<String, PackageGraphNode>(); Iterator<String> merges = packageIdToPackageMergeMap.keySet().iterator(); while (merges.hasNext()) { PackageGraphNode target = packageIdToPackageMergeMap.get(merges.next()); Package targetPackage = (Package)this.getElementById(target.getId()); target.setPackage(targetPackage); // HACKY but we must build the merge graph during mapping, and all package targets don't yet exist if (target.getNodes() == null) continue; Iterator<PackageGraphNode> iter = target.getNodes().iterator(); while (iter.hasNext()) { PackageGraphNode source = iter.next(); Package sourcePackage = (Package)this.getElementById(source.getId()); source.setPackage(sourcePackage); sources.put(source.getId(), source); } } List<PackageGraphNode> roots = new ArrayList<PackageGraphNode>(); merges = packageIdToPackageMergeMap.keySet().iterator(); while (merges.hasNext()) { PackageGraphNode target = packageIdToPackageMergeMap.get(merges.next()); if (sources.get(target.getId()) != null) // some source points to it, not a root continue; roots.add(target); } if (log.isDebugEnabled()) log.debug("found " + String.valueOf(roots.size()) + " merge-root and " + String.valueOf(sources.size()) + " source packages"); return roots; } @SuppressWarnings("unchecked") private RepositoryConfig unmarshalConfig(String configFileName, RepositoryConfigDataBinding binding) { try { InputStream stream = Element.class.getResourceAsStream(configFileName); if (stream == null) stream = Element.class.getClassLoader().getResourceAsStream(configFileName); if (stream == null) throw new RepositorylException("cannot find resource '" + configFileName + "'"); JAXBElement root = (JAXBElement) binding.validate(stream); RepositoryConfig result = (RepositoryConfig) root.getValue(); return result; } catch (UnmarshalException e) { throw new RepositorylException(e); } catch (JAXBException e) { throw new RepositorylException(e); } } protected void collectAttributes(Class_ clss, List<Property> attributes) { fUML.Syntax.Classes.Kernel.Property[] props = new fUML.Syntax.Classes.Kernel.Property[clss.getDelegate().ownedAttribute.size()]; clss.getDelegate().ownedAttribute.toArray(props); for (int i = 0; i < props.length; i++) { fUML.Syntax.Classes.Kernel.Property fumlProperty = props[i]; Property property = new org.modeldriven.fuml.repository.model.Property(fumlProperty, clss.getArtifact()); attributes.add(property); } for (Classifier generalization : clss.getGeneralization()) { String superclassXmiId = generalization.getXmiId(); Element element = elementIdToElementMap.get(superclassXmiId); if (element == null) { throw new InvalidReferenceException(superclassXmiId); // log.warn("invalid reference: " + superclassXmiId); // continue; } Class_ umlSuperClass = (Class_) element; collectAttributes(umlSuperClass, attributes); } } protected void collectOperations(Class_ clss, List<Operation> operations) { Iterator<Operation> iter = clss.getDelegate().ownedOperation.iterator(); while (iter.hasNext()) { Operation oper = iter.next(); operations.add(oper); } for (Classifier generalization : clss.getGeneralization()) { String superclassXmiId = generalization.getXmiId(); Element element = elementIdToElementMap.get(superclassXmiId); if (element == null) { throw new InvalidReferenceException(superclassXmiId); // log.warn("invalid reference: " + superclassXmiId); // continue; } Class_ umlSuperClass = (Class_) element; collectOperations(umlSuperClass, operations); } } public RepositoryMapping getMapping() { return this; } public String getDefaultUMLNamespaceURI() { return this.config.getDefaultUMLNamespaceURI(); } public Classifier getClassifier(String name) { return getClassifier(name, false); } public Classifier findClassifier(String name) { return getClassifier(name, true); } private Classifier getClassifier(String name, boolean supressErrors) { Classifier result = null; if (name.indexOf(".") == -1 && name.indexOf("#") == -1) { result = this.classifierNameToClassifierMap.get(name); } else result = this.qualifiedClassifierNameToClassifierMap.get(name); if (result == null && !supressErrors) throw new RepositorylException("no classifier found for name, '" + name + "'"); return result; } public Classifier[] getAllClassifiers() { Iterator<String> iter = this.classifierNameToClassifierMap.keySet().iterator(); List<Classifier> list = new ArrayList<Classifier>(); while (iter.hasNext()) list.add(this.classifierNameToClassifierMap.get(iter.next())); Classifier[] result = new Classifier[list.size()]; list.toArray(result); return result; } public List<Extension> getExtensions(Element element) { List<Extension> result = this.elementToExtensionListMap.get(element.getXmiId()); if (result != null) return result; else return new ArrayList<Extension>(); } public List<Stereotype> getStereotypes(Element element) { List<Stereotype> result = this.elementToStereotypeListMap.get(element.getXmiId()); if (result != null) return result; else return new ArrayList<Stereotype>(); } public List<Stereotype> getStereotypes(Class<?> clss) { List<Stereotype> result = this.classToStereotypeListMap.get(clss); if (result != null) return result; else return new ArrayList<Stereotype>(); } public List<Stereotype> getAllStereotypes() { List<Stereotype> result = new ArrayList<Stereotype>(); for (List<Stereotype> sublist : this.elementToStereotypeListMap.values()) { result.addAll(sublist); } return result; } public String getJavaPackageNameForClass(Classifier classifier) { return getJavaPackageNameForClass(classifier, false); } public String findJavaPackageNamePackageForClass(Classifier classifier) { return getJavaPackageNameForClass(classifier, true); } private String getJavaPackageNameForClass(Classifier classifier, boolean supressErrors) { String classifierName = classifier.getName(); String result = null; if (classifierName.indexOf(".") == -1) { for (ExtensionPackage extPkg : FumlConfiguration.getInstance().getConfig().getImportConfiguration().getExtensionPackage()) { String qualifiedClassName = extPkg.getName() + "." + classifierName; try { Class.forName(qualifiedClassName); result = extPkg.getName(); break; } catch (ClassNotFoundException e) { } } if (result == null) { result = this.classifierNameToPackageNameMap.get(classifierName); } } else result = this.qualifiedClassifierNameToPackageNameMap.get(classifierName); if (result == null && !supressErrors) throw new XmiException("no package found for class '" + classifierName + "'"); return result; } public boolean isIgnoredClassifier(String classifierName) { return this.ignoredClassNameMap.get(classifierName) != null; } public boolean isIgnoredClassifier(Classifier classifier) { String packageName = getJavaPackageNameForClass(classifier); if (isIgnoredPackage(packageName)) return true; else return this.ignoredClassNameMap.get(classifier.getName()) != null; } public boolean isIgnoredPackage(String packageName) { return this.ignoredPackageNameMap.get(packageName) != null; } public static void main(String[] args) { InMemoryRepository.getInstance(); } public List<Classifier> getSpecializations(Classifier classifier) { List<Classifier> result = this.classifierIdToSpecializationClassifierMap.get( classifier.getXmiId()); if (result != null) return result; else return EMPTY_CLASSIFIER_LIST; } }