/* * 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.xmi; import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.modeldriven.fuml.xmi.stream.StreamNode; import fUML.Syntax.Classes.Kernel.Enumeration; import fUML.Syntax.Classes.Kernel.PrimitiveType; import org.modeldriven.fuml.repository.Class_; import org.modeldriven.fuml.repository.Classifier; import org.modeldriven.fuml.repository.Repository; import org.modeldriven.fuml.repository.Property; /** * Stateless model-related logic delegate class. Handles metadata related logic specific * for the the XMI package or it's sub-packages. * * @author Scott Cinnamond */ public class ModelSupport { private static Log log = LogFactory.getLog(ModelSupport.class); public Classifier findClassifier(XmiNode target) { // First assume the XMI element is not a class name, is an association-end // which is the most common case, and we find the type using the // xmi:type attribute which was already set in the target node String name = target.getXmiType(); if (name == null || name.trim().length() == 0) name = target.getLocalName(); return findClassifierByNamespace(target, name); } public Classifier findClassifier(XmiNode target, Class_ sourceClassifier) { Classifier result = null; // For nodes with an XMI type attrib, don't ignore it // if no classifier found for type. Could result in // abstract class being used based on default // type determination below. String xmiType = target.getXmiType(); if (xmiType != null && xmiType.length() > 0) { result = findClassifierByNamespace(target, xmiType); } else { Property property = sourceClassifier.findProperty(target.getLocalName()); if (property != null) { Classifier type = property.getType(); if (type != null) { String name = type.getName(); result = Repository.INSTANCE.getClassifier(name); // If the property type is Type or Classifier, and the target is // not an internal or external reference, then it must be a // reference to a standard PrimitiveType without an xmi:type // given. if ((name.equals("Type") || name.equals("Classifier")) && !isInternalReferenceElement((StreamNode)target, result, false) && !isExternalReferenceElement((StreamNode)target, result, false)) { result = Repository.INSTANCE.getClassifier("PrimitiveType"); } } } } return result; } private Classifier findClassifierByNamespace(XmiNode target, String name) { String uri = target.getNamespaceURI(); if (uri == null || uri.length() == 0) throw new XmiException("no namespace URI found for '" + name + "'"); // try the XMI node/element specific namespace if (target.getPrefix() != null) uri = target.getNamespaceURI(); String qualifiedName = uri + "#" + name; Classifier result = Repository.INSTANCE.findClassifier(qualifiedName); // try the default UML URI if (result == null) { qualifiedName = Repository.INSTANCE.getDefaultUMLNamespaceURI() + "#" + name; result = Repository.INSTANCE.findClassifier(qualifiedName); } return result; } public boolean isNotReferenceElement(XmiNode node, Classifier classifier, boolean hasAttributes) { // if non-reference primitive or enumeration type property element Class<?> class_ = classifier.getDelegate().getClass(); if (PrimitiveType.class.isAssignableFrom(class_) || Enumeration.class.isAssignableFrom(class_)) { if (node.getNodes() != null && node.getNodes().size() > 0) log.warn("found child nodes(s) under primitive type or enumertion, " + classifier.getName()); if (hasAttributes) log.warn("found attribute(s) for primitive type or enumeration, " + classifier.getName()); return true; // it's a non-reference property, "can't" have attributes } return false; } /** * Returns true if an element is not a primitive type and it has characters * or an ideref XMI attribute. This constitutes an internal reference element. * @param node * @param classifier * @param hasAttributes * @return */ public boolean isInternalReferenceElement(XmiNode node, Classifier classifier, boolean hasAttributes) { if (!isNotReferenceElement(node, classifier, hasAttributes)) { if (node.hasCharacters()) { if (hasAttributes) log.warn("found attribute(s) for characters node of type, " + classifier.getName()); } else { StreamNode eventNode = (StreamNode)node; QName idref = new QName(eventNode.getContext().getXmiNamespace().getNamespaceURI(), XmiConstants.ATTRIBUTE_XMI_IDREF); if (node.hasAttribute(idref)) return true; } } return false; } /** * If not a primitive type and we have an href attrib (??). * @param node * @param classifier * @param hasAttributes * @return */ public boolean isExternalReferenceElement(XmiNode node, Classifier classifier, boolean hasAttributes) { // has to be a ref boolean result = false; if (!isNotReferenceElement(node, classifier, hasAttributes)) { QName href = new QName(XmiConstants.ATTRIBUTE_XMI_HREF); if (node.hasAttribute(href)) { String hrefValue = node.getAttributeValue(href); if (hrefValue == null) return true; // FIXME: support Primitive Type references found in an href attribute // as external references. int idx = hrefValue.lastIndexOf("#"); String suffix = hrefValue.substring(idx+1); if (suffix.equals("Integer") || suffix.equals("Real") || suffix.equals("String") || suffix.equals("Boolean") || suffix.equals("UnlimitedNatural")) { return false; } else { return true; } } } return result; } public boolean isReferenceAttribute(Property property) { Classifier typeClassifier = property.getType(); if (!PrimitiveType.class.isAssignableFrom(typeClassifier.getDelegate().getClass()) && !Enumeration.class.isAssignableFrom(typeClassifier.getDelegate().getClass())) { return true; } return false; }}