blob: 1a5a5cb9af4a3c0baddade123a7c459ffd013703 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 David A Carlson.
* 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:
* David A Carlson (XMLmodeling.com) - initial API and implementation
*
* $Id$
*******************************************************************************/
package org.eclipse.mdht.uml.common.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.mdht.uml.common.internal.Logger;
import org.eclipse.mdht.uml.common.modelfilter.ModelFilterUtil;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.UMLPackage;
public class ModelFilter {
private Package sourcePackage;
private Package filteredPackage;
private Map<String, Class> filteredMapping;
public ModelFilter(Package sourcePackage, Package filteredPackage) {
this.sourcePackage = sourcePackage;
this.filteredPackage = filteredPackage;
filteredMapping = new HashMap<String, Class>();
// assure that all proxies are resolved.
if (sourcePackage != null) {
EcoreUtil.resolveAll(sourcePackage.eResource());
}
if (filteredPackage != null) {
EcoreUtil.resolveAll(filteredPackage.eResource());
}
// attempt to load XSD DataTypes library, if available
URI xsdDatatypesURI = URI.createURI("pathmap://XMLmodeling_LIBRARIES/XSDDataTypes.library.uml");
sourcePackage.eResource().getResourceSet().getResource(xsdDatatypesURI, true);
assignFilteredNamespaceURI(sourcePackage, filteredPackage);
}
public Package getFilteredPackage() {
return filteredPackage;
}
protected boolean isDefaultHidden(NamedElement element) {
boolean hidden = false;
return hidden;
}
protected boolean isDefaultCollapsed(NamedElement element) {
boolean collapsed = false;
return collapsed;
}
public boolean isHidden(NamedElement element) {
if (ModelFilterUtil.isHidden(element)) {
return true;
} else if (ModelFilterUtil.isShown(element)) {
return false;
} else if (ModelFilterUtil.isCollapsed(element)) {
return false;
} else {
return isDefaultHidden(element);
}
}
public boolean isCollapsed(NamedElement element) {
return ModelFilterUtil.isCollapsed(element)
? true
: isDefaultCollapsed(element);
}
public void filterPackage(Package pkg) {
try {
TreeIterator<EObject> iterator = EcoreUtil.getAllContents(Collections.singletonList(pkg));
while (iterator != null && iterator.hasNext()) {
EObject child = iterator.next();
if (child instanceof Class) {
filterClass((Class) child);
}
}
} catch (IndexOutOfBoundsException e) {
Logger.logException(e);
}
}
public Class filterClass(Class sourceClass) {
if (isHidden(sourceClass)) {
// omit this class from filtered model
return null;
}
Class filteredClass = filteredMapping.get(EcoreUtil.getURI(sourceClass).toString());
if (filteredClass == null) {
// inner classes were previously copied as content of parent class
if (sourceClass.getOwner() instanceof Class) {
Class filteredOwner = filteredMapping.get(EcoreUtil.getURI(sourceClass.getOwner()).toString());
if (filteredOwner != null) {
filteredClass = (Class) filteredOwner.getNestedClassifier(sourceClass.getName());
}
}
if (filteredClass == null) {
filteredClass = EcoreUtil.copy(sourceClass);
filteredPackage.getOwnedTypes().add(filteredClass);
UMLUtil.cloneStereotypes(sourceClass, filteredClass);
}
filteredMapping.put(EcoreUtil.getURI(sourceClass).toString(), filteredClass);
// rename using business name (use sourceClass to get corresponding .properties)
String businessName = getFilteredClassName(sourceClass);
if (businessName != null) {
filteredClass.setName(businessName);
}
List<Property> sourceAttributes = new ArrayList<Property>(sourceClass.getOwnedAttributes());
for (Property property : sourceAttributes) {
processProperty(property, filteredClass);
}
// remove all constraints from filtered model
List<Constraint> constraints = new ArrayList<Constraint>(filteredClass.getOwnedRules());
for (Constraint constraint : constraints) {
constraint.destroy();
}
}
return filteredClass;
}
protected void processProperty(Property property, Class filteredClass) {
String businessName = getFilteredPropertyName(property);
Property mappedProperty = filteredClass.getOwnedAttribute(property.getName(), null);
if (mappedProperty == null) {
mappedProperty = filteredClass.getOwnedAttribute(businessName, null);
if (mappedProperty == null) {
return;
}
}
// remove hidden properties
if (isHidden(property)) {
filteredClass.getOwnedAttributes().remove(mappedProperty);
mappedProperty.destroy();
return;
}
// process collapsed properties
if (isCollapsed(property)) {
List<Property> collapsedContent = getCollapsedContent(property);
// remove original property and replace with collapsed content
filteredClass.getOwnedAttributes().remove(mappedProperty);
mappedProperty.destroy();
for (Property collapsedProperty : collapsedContent) {
// clone the property
Property clonedProperty = EcoreUtil.copy(collapsedProperty);
filteredClass.getOwnedAttributes().add(clonedProperty);
UMLUtil.cloneStereotypes(collapsedProperty, clonedProperty);
// collapsed property cannot have lower bound multiplicity greater than its parent
if (clonedProperty.getLower() > property.getLower()) {
clonedProperty.setLower(property.getLower());
}
// process collapsed content recursively
// use the source property to gain access to original structure and filtering criteria
processProperty(collapsedProperty, filteredClass);
// if only one collapsed property, rename to same as parent
// do this last, to not interfere with processing property in previous step
if (collapsedContent.size() == 1) {
clonedProperty.setName(businessName);
}
}
return;
}
// rename using business name (use sourceClass property to get corresponding .properties)
mappedProperty.setName(businessName);
// if property type is a class in the same package, create filtered class as new property type
Type filteredType = null;
if (property.getType() instanceof Class &&
property.getType().getNearestPackage() == property.getNearestPackage()) {
filteredType = filterClass((Class) property.getType());
mappedProperty.setType(filteredType);
} else {
filteredType = property.getType();
}
// copy association
if (property.getAssociation() != null) {
Association assocClone = (Association) filteredPackage.createOwnedType(
null, UMLPackage.Literals.ASSOCIATION);
assocClone.getMemberEnds().add(mappedProperty);
Property ownedEnd = UMLFactory.eINSTANCE.createProperty();
ownedEnd.setType(filteredClass);
assocClone.getOwnedEnds().add(ownedEnd);
UMLUtil.cloneStereotypes(property.getAssociation(), assocClone);
} else {
// property type may be replaced with a specialized type, e.g. an Enumeration
// use source property to allow for resource properties file lookup
Classifier newType = getFilteredPropertyType(property);
if (newType != null) {
mappedProperty.setType(newType);
}
}
}
protected boolean isXMLAttribute(Property property) {
Stereotype eAttribute = property.getAppliedStereotype("Ecore::EAttribute");
return (eAttribute != null)
? true
: false;
}
protected String normalizeCodeName(String name) {
String result = "";
String[] parts = name.split(" ");
for (String part : parts) {
result += part.substring(0, 1).toUpperCase() + part.substring(1);
}
return result;
}
protected void assignFilteredNamespaceURI(Package sourcePackage, Package filteredPackage) {
// may be implemented by subclasses
}
protected String getFilteredClassName(Class umlClass) {
String businessName = NamedElementUtil.getBusinessName(umlClass);
if (businessName != null) {
businessName = normalizeCodeName(businessName);
businessName = UML2Util.getValidJavaIdentifier(businessName);
businessName = businessName.substring(0, 1).toUpperCase() + businessName.substring(1);
}
return businessName;
}
protected String getFilteredPropertyName(Property property) {
String businessName = NamedElementUtil.getBusinessName(property);
if (businessName != null) {
businessName = normalizeCodeName(businessName);
businessName = UML2Util.getValidJavaIdentifier(businessName);
businessName = businessName.substring(0, 1).toLowerCase() + businessName.substring(1);
}
return businessName;
}
protected Classifier getFilteredPropertyType(Property property) {
return getPropertyTypeReplacement(property);
}
protected List<Property> getCollapsedContent(Property property) {
List<Property> collapsedContent = new ArrayList<Property>();
if (property.getType() instanceof Class) {
for (Property nestedProperty : ((Class) property.getType()).getOwnedAttributes()) {
if (!isHidden(nestedProperty)) {
collapsedContent.add(nestedProperty);
}
}
}
return collapsedContent;
}
protected Classifier getPropertyTypeReplacement(Property property) {
String qualifiedName = ModelFilterUtil.getTypeReplacement(property);
if (qualifiedName != null) {
ResourceSet resourceSet = property.eResource().getResourceSet();
Collection<NamedElement> elements = org.eclipse.uml2.uml.util.UMLUtil.findNamedElements(
resourceSet, qualifiedName);
for (NamedElement namedElement : elements) {
if (namedElement instanceof Classifier) {
return (Classifier) namedElement;
}
}
}
return null;
}
}