/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package de.crowdcode.kissmda.core.uml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.InterfaceRealization;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameterSubstitution;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.ValueSpecification;
import de.crowdcode.kissmda.core.jdt.DataTypeUtils;
/**
* This class is taken from JavaBasic-Generator Fornax oAW project. Extended by
* Lofi Dewanto for KissMDA.
*
* Helper class for the helper extension of the JavaBasic-Generator. See:
* http://goo.gl/W13i8
*
* @author Thorsten Kamann <thorsten.kamann@googlemail.com>
* @author Andre Neumann <andre.v.neumann@gmx.de>
* @author Lofi Dewanto
*
* @since 1.0.0
*/
public class UmlHelper {
private static final Logger logger = Logger.getLogger(UmlHelper.class
.getName());
@Inject
private DataTypeUtils dataTypeUtils;
private Map<String, String> annotationSourceKeyMap = null;
public void log(Object o) {
logger.log(Level.FINE, o != null ? o.toString() : "<null>");
}
public void log(Object o, String logMsg) {
logger.log(Level.FINE, logMsg + ": " + o != null ? o.toString()
: "<null>");
}
/**
* Calculate the full qualified packagename of the given type.
*
* @param type
* The <code>org.eclipse.uml2.uml.Type</code> to calculate the
* full qualified name for
* @return The full qualified name as <code>java.lang.String</code>
*/
public String getFQNPackageName(Type type) {
String pn = "";
Package p = findNearestPackage(type);
while (p != null) {
pn = p.getName() + "." + pn;
p = findNearestPackage(p);
}
if (pn.endsWith(".")) {
pn = pn.substring(0, pn.length() - 1);
}
return pn;
}
/**
* Finds the nearest Package for the given element.
*
* @param el
* The <code>org.eclipse.uml2.uml.Element</code> to find the
* nearest package for
* @return The nearest Package or null
*/
private Package findNearestPackage(Element el) {
Element o = el.getOwner();
while (!(o instanceof Package) && (o != null)) {
o = o.getOwner();
}
if (o instanceof Package) {
if (o instanceof Model || o instanceof Profile) {
return null;
} else {
return (Package) o;
}
} else {
return null;
}
}
/**
* Calculate the full qualified packagepath of the given type.
*
* @param type
* The <code>org.eclipse.uml2.uml.Type</code> to calculate the
* full qualified path for
* @return The full qualified path as <code>java.lang.String</code>
*/
public String getFQNPackagePath(Type type) {
String path = getFQNPackageName(type);
path = path.replaceAll("\\.", "/");
return path;
}
/**
* Calculate the full qualified componentname of the given type.
*
* @param type
* The <code>org.eclipse.uml2.uml.Type</code> to calculate the
* full qualified name for
* @return The full qualified name as <code>java.lang.String</code>
*/
public String getFQNComponentName(Type type) {
String pn = "";
Component cp = null;
// looking for start component
if (type instanceof Component) {
cp = (Component) type;
} else {
cp = findNearestComponent(type);
}
// stepping up component hierarchy
while (cp != null) {
pn = cp.getName() + "." + pn;
cp = findNearestComponent(cp);
}
if (pn.endsWith(".")) {
pn = pn.substring(0, pn.length() - 1);
}
return pn;
}
/**
* Calculate the full qualified componentpath of the given type.
*
* @param type
* The <code>org.eclipse.uml2.uml.Type</code> to calculate the
* full qualified path for
* @return The full qualified path as <code>java.lang.String</code>
*/
public String getFQNComponentPath(Type type) {
String path = getFQNComponentName(type);
path = path.replaceAll("\\.", "/");
return path;
}
/**
* Finds the nearest Component for the given element.
*
* @param el
* The <code>org.eclipse.uml2.uml.Element</code> to find the
* nearest component for
* @return The nearest Component or null
*/
private Component findNearestComponent(Element el) {
Element o = el.getOwner();
while (!(o instanceof Component) && (o != null)) {
o = o.getOwner();
}
if (o != null) {
return (Component) o;
} else {
return null;
}
}
/**
* Get XmiId.
*
* @param eObject
* The object to retrieve the id from
* @return The unique XMI-ID of the given element
*/
public String getXmiId(EObject eObject) {
return ((XMLResource) eObject.eResource()).getID(eObject);
}
/**
* Collects all attributes the classifier and their interfaces have.
*
* @param classifier
* The classifier the attributes should be collected
* @return An <code>EList</code> with all collected attributes
*/
public EList<Property> getAllAttributes(Classifier classifier) {
EList<Property> attributes = new UniqueEList<Property>();
attributes.addAll(classifier.getAttributes());
if (classifier instanceof org.eclipse.uml2.uml.Class) {
Class clazz = (Class) classifier;
for (int i = 0; i < clazz.getImplementedInterfaces().size(); i++) {
attributes.addAll(getAllInterfaceAttributes(clazz
.getImplementedInterfaces().get(i)));
}
}
return attributes;
}
/**
* Collects all operations the classifier and their interfaces have.
*
* @param classifier
* The classifier the operations should be collected
* @return An <code>EList</code> with all collected operations
*/
public EList<Operation> getAllOperations(Classifier classifier) {
EList<Operation> operations = new UniqueEList<Operation>();
operations.addAll(classifier.getOperations());
if (classifier instanceof org.eclipse.uml2.uml.Class) {
Class clazz = (Class) classifier;
for (int i = 0; i < clazz.getImplementedInterfaces().size(); i++) {
operations.addAll(getAllInterfaceOperations(clazz
.getImplementedInterfaces().get(i)));
}
}
return operations;
}
/**
* Collects all dependencies the NamedElement has. In the case of a Class
* the dependencies of its interfaces will be processed too.
*
* @param element
* The NamedElement the dependencies should be collected
* @return An <code>EList</code> with all collected dependencies
*/
@SuppressWarnings("unchecked")
public EList<Dependency> getAllDependencies(NamedElement element) {
EList<Dependency> dependencies = new UniqueEList<Dependency>();
dependencies.addAll(element.getClientDependencies());
if (element instanceof org.eclipse.uml2.uml.Class) {
Class clazz = (Class) element;
for (int i = 0; i < clazz.getImplementedInterfaces().size(); i++) {
dependencies.addAll(getAllInterfaceAssociations(clazz
.getImplementedInterfaces().get(i)));
}
}
EList<Dependency> depsFiltered = new UniqueEList<Dependency>();
for (Iterator<Dependency> iter = dependencies.iterator(); iter
.hasNext();) {
Dependency e = iter.next();
if (!(e instanceof InterfaceRealization)) {
depsFiltered.add(e);
}
}
return depsFiltered;
}
/**
* Collects all associations the classifier and their interfaces have.
*
* @param classifier
* The classifier the associations should be collected
* @return An <code>EList</code> with all collected associations
*/
@SuppressWarnings("unchecked")
public EList<Association> getAllAssociations(Classifier classifier) {
EList<Association> associations = new UniqueEList<Association>();
associations.addAll(classifier.getAssociations());
if (classifier instanceof org.eclipse.uml2.uml.Class) {
Class clazz = (Class) classifier;
for (int i = 0; i < clazz.getImplementedInterfaces().size(); i++) {
associations.addAll(getAllInterfaceAssociations(clazz
.getImplementedInterfaces().get(i)));
}
}
return associations;
}
/**
* Collects all attributes of the given interface needed to implement
* (creating fields and getter/setter methods) by a implementing class.
*
* @param iFace
* The interface to collect the operations from
* @return An <code>EList</code> with all collected attributes
*/
private EList<Property> getAllInterfaceAttributes(Interface iFace) {
EList<Property> attributes = new UniqueEList<Property>();
attributes.addAll(iFace.getAllAttributes());
if (iFace.getGeneralizations().size() > 0) {
for (int i = 0; i < iFace.getGeneralizations().size(); i++) {
attributes.addAll(getAllInterfaceAttributes((Interface) iFace
.getGeneralizations().get(i).getGeneral()));
}
}
return attributes;
}
/**
* Collects all operation of the given interface needed to implement by a
* implementing class.
*
* @param iFace
* The interface to collect the operations from
* @return An <code>EList</code> with all collected operations
*/
private EList<Operation> getAllInterfaceOperations(Interface iFace) {
EList<Operation> operations = new UniqueEList<Operation>();
operations.addAll(iFace.getAllOperations());
if (iFace.getGeneralizations().size() > 0) {
for (int i = 0; i < iFace.getGeneralizations().size(); i++) {
operations.addAll(getAllInterfaceOperations((Interface) iFace
.getGeneralizations().get(i).getGeneral()));
}
}
return operations;
}
/**
* Collects all associations of the given interface needed to implement by a
* implementing class.
*
* @param iFace
* The interface to collect the associations from
* @return An <code>EList</code> with all collected associations
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private EList getAllInterfaceAssociations(Interface iFace) {
EList associations = new UniqueEList();
associations.addAll(iFace.getAssociations());
if (iFace.getGeneralizations().size() > 0) {
for (int i = 0; i < iFace.getGeneralizations().size(); i++) {
associations
.addAll(getAllInterfaceAssociations((Interface) iFace
.getGeneralizations().get(i).getGeneral()));
}
}
return associations;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public String formatComment(Element element, String prefix,
String propertyFileName) {
String comment = "";
EList comments = new BasicEList();
createAnnotationSourceKey(propertyFileName);
for (Iterator<String> iter = annotationSourceKeyMap.keySet().iterator(); iter
.hasNext();) {
String source = iter.next();
String key = annotationSourceKeyMap.get(source);
comment = findDocumentationAnnotation(element, source, key);
if (comment != null) {
break;
}
}
if (comment != null) {
comments.add(comment);
} else {
comments = element.getOwnedComments();
}
return getFormatedComment(comments, prefix, null);
}
public boolean createAnnotationSourceKey(String propertyFileName) {
String[] items;
String source;
String key;
if (annotationSourceKeyMap != null) {
return false;
}
annotationSourceKeyMap = new HashMap<String, String>();
items = propertyFileName.split(",");
for (int i = 0; i < items.length; i++) {
String item = items[i];
item = item.trim();
int start = item.indexOf("[");
int end = item.indexOf("]");
if (start < 0 || end < start) {
source = item;
key = "documentation";
} else {
source = item.substring(0, start);
key = item.substring(start + 1, end);
}
annotationSourceKeyMap.put(source, key);
}
return true;
}
private String findDocumentationAnnotation(Element parent, String source,
String key) {
EAnnotation ea = parent
.getEAnnotation("http://www.topcased.org/documentation");
String comment = null;
if (ea != null) {
EMap<?, ?> details = ea.getDetails();
if (details != null && !details.isEmpty()
&& details.get("documentation") != null) {
comment = details.get("documentation").toString();
}
}
return comment;
}
/**
* A method comment is extended by the description of the parameters.
*
* @precondition Iff the parameter is a return type, the type of the
* parameter must be not null.
*
* @param parameter
* @param prefix
* @param annotation
* @param propertyFileName
* @return The line in the comment describing given parameter
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public String formatParameterComment(Parameter parameter, String prefix,
String annotation, String propertyFileName) {
String comment = "";
EList comments = new BasicEList();
createAnnotationSourceKey(propertyFileName);
for (Iterator iter = annotationSourceKeyMap.keySet().iterator(); iter
.hasNext();) {
String source = (String) iter.next();
String key = annotationSourceKeyMap.get(source);
comment = findDocumentationAnnotation(parameter, source, key);
if (comment != null) {
break;
}
}
if (comment != null) {
comments.add(comment);
} else {
comments = parameter.getOwnedComments();
}
if (comments.isEmpty()) {
comment = prefix
+ "@"
+ annotation
+ " "
+ ((parameter.getDirection()
.equals(ParameterDirectionKind.RETURN_LITERAL)) ? "<code>"
+ parameter.getType().getName() + "</code>"
: parameter.getName());
} else {
comment = getFormatedComment(
comments,
prefix,
annotation
+ " "
+ ((parameter.getDirection()
.equals(ParameterDirectionKind.RETURN_LITERAL)) ? ""
: parameter.getName()));
}
return comment;
}
public String getDefaultValue(Property property) {
ValueSpecification vs = property.getDefaultValue();
if (vs == null) {
if (property.getType() instanceof PrimitiveType) {
PrimitiveType pt = (PrimitiveType) property.getType();
if (pt.getName().equals("int")) {
return "0";
}
}
return "null";
} else {
if (vs.getType() == null) {
return vs.stringValue();
} else if (vs.getType().getName().equals("String")) {
return vs.stringValue();
} else if (vs.getType().getName().equals("Integer")) {
return vs.integerValue() + "";
} else if (vs.getType().getName().equals("Boolean")) {
return vs.booleanValue() + "";
}
return vs.stringValue();
}
}
private String getFormatedComment(EList<?> comments, String prefix,
String annotation) {
String s = "";
if (!comments.isEmpty()) {
for (int i = 0; i < comments.size(); i++) {
Object commentsObject = comments.get(i);
String text = "";
if (commentsObject instanceof String) {
text = (String) commentsObject;
} else if (commentsObject instanceof Comment) {
Comment c = (Comment) comments.get(i);
text = c.getBody();
}
if (text != null && !"".equals(text)) {
s = doFormatting(text, s, prefix, annotation);
} else {
s = "* ";
}
}
} else {
s = "* ";
}
return s;
}
private String doFormatting(String text, String appendTo, String prefix,
String annotation) {
if (!"".equals(appendTo)) {
appendTo += "\n";
}
text = text.replaceAll("\\n", "\n" + prefix);
appendTo += prefix;
if (annotation != null && !"".equals(annotation)) {
appendTo += "@" + annotation;
}
appendTo += " " + text;
return appendTo;
}
/**
* This method takes a string and try's to cut it down into its peaces. The
* character given as delimiter will be used to determine the single peaces.
*
* @param thwString
* .
* @return theList
* @since 2.1.0
*/
public EList<String> convertStringToList(String theString, String delimiter) {
if (delimiter == null || delimiter.equals("")) {
delimiter = ",";
}
StringTokenizer tokenizer = new StringTokenizer(theString, delimiter);
EList<String> theList = new UniqueEList<String>(tokenizer.countTokens());
String currentToken = null;
while (tokenizer.hasMoreTokens()) {
currentToken = tokenizer.nextToken();
if (StringUtils.isNotEmpty(currentToken)) {
theList.add(currentToken.trim());
}
}
return theList;
}
/**
* Get the list of template parameter substitution.
*
* @param type
* UML2 type
* @return List of all UML2 ParameterableElement
*/
public List<String> getTemplateParameterSubstitution(Type type) {
List<String> results = new ArrayList<String>();
// We need to take care of following cases:
// -> Data::datatype-bindings::Collection<Company>
// -> Data::de::crowdcode::kissmda::testapp::Collection<Person>
// We need to have a full qualified name for the Type in
// Collection<Type>. Something like
// -> Data::datatype-bindings::Collection<de.test.Company>
logger.log(Level.FINE,
"getTemplateParameterSubstitution: " + type.getQualifiedName()
+ " - " + type.getTemplateParameter());
EList<Element> elements = type.allOwnedElements();
for (Element element : elements) {
if (element instanceof TemplateBinding) {
TemplateBinding templateBinding = (TemplateBinding) element;
EList<TemplateParameterSubstitution> subs = templateBinding
.getParameterSubstitutions();
for (TemplateParameterSubstitution templateParameterSubstitution : subs) {
ParameterableElement paramElement = templateParameterSubstitution
.getActual();
if (paramElement instanceof Classifier) {
Classifier clazzifier = (Classifier) paramElement;
if (!dataTypeUtils
.isPrimitiveType(clazzifier.getName())
&& !dataTypeUtils.isJavaType(clazzifier
.getName())) {
results.add(clazzifier.getQualifiedName());
} else {
results.add(clazzifier.getName());
}
}
}
}
}
return results;
}
/**
* Check for parameterized type to get the template parameter substitution.
*
* @param type
* UML2 type
* @return Map of umlTypeName and umlQualifiedTypeName
*/
public Map<String, String> checkParameterizedTypeForTemplateParameterSubstitution(
Type type) {
Map<String, String> results = new HashMap<String, String>();
String umlTypeName = type.getName();
String umlQualifiedTypeName = type.getQualifiedName();
List<String> templateSubstitutions = getTemplateParameterSubstitution(type);
if (templateSubstitutions.size() != 0) {
int index = 0;
String paramTypeNames = StringUtils
.substringAfter(umlTypeName, "<");
paramTypeNames = StringUtils.removeEnd(paramTypeNames, ">");
EList<String> paramTypeNameList = convertStringToList(
paramTypeNames, ",");
for (String paramTypeName : paramTypeNameList) {
umlTypeName = StringUtils.replace(umlTypeName, paramTypeName,
templateSubstitutions.get(index));
umlQualifiedTypeName = StringUtils.replace(
umlQualifiedTypeName, paramTypeName,
templateSubstitutions.get(index));
index = index + 1;
}
}
results.put("umlTypeName", umlTypeName);
results.put("umlQualifiedTypeName", umlQualifiedTypeName);
return results;
}
}