| /******************************************************************************* |
| * Copyright (c) 2013 THALES GLOBAL SERVICES and Others |
| * 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: |
| * Obeo - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.emf.ecoretools.design.service; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EGenericType; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature.Setting; |
| import org.eclipse.emf.ecore.ETypeParameter; |
| import org.eclipse.emf.ecore.ETypedElement; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| import org.eclipse.sirius.business.api.session.Session; |
| import org.eclipse.sirius.business.api.session.SessionManager; |
| import org.eclipse.sirius.diagram.DEdge; |
| import org.eclipse.sirius.diagram.DSemanticDiagram; |
| import org.eclipse.sirius.viewpoint.DSemanticDecorator; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * Services dealing with EReferences usable from a VSM. |
| */ |
| public class EReferenceServices { |
| private static final String CARDINALITY_SEPARATOR = ".."; |
| |
| public static final String EOPPOSITE_SEPARATOR = " - "; |
| |
| public static final String DERIVED_PREFIX = "/"; |
| |
| public static final String CARDINALITY_UNBOUNDED = "*"; |
| |
| public static final String CARDINALITY_UNBOUNDED_ALTERNATIVE = "-1"; |
| |
| private static final String TYPE_SEPARATOR = ":"; |
| |
| /** |
| * Return the displayed label for an EReference eOpposite. |
| */ |
| public String getEOppositeEReferenceName(EReference ref) { |
| if (ref.getEOpposite() != null) { |
| return render(ref.getEOpposite()) + EOPPOSITE_SEPARATOR + render(ref); |
| } else { |
| return ""; |
| } |
| } |
| |
| public List<EReference> getInverseEReferences(EObject ctx) { |
| Session sess = SessionManager.INSTANCE.getSession(ctx); |
| List<EReference> result = Lists.newArrayList(); |
| if (sess != null) { |
| for (Setting setting : sess.getSemanticCrossReferencer().getInverseReferences(ctx)) { |
| if (setting.getEObject() instanceof EReference) |
| result.add((EReference) setting.getEObject()); |
| } |
| } |
| return result; |
| } |
| |
| public void setEType(EObject host, EObject target) { |
| if (host instanceof ETypedElement) { |
| EGenericsServices.setETypeWithGenerics((ETypedElement) host, target); |
| } |
| } |
| |
| public String eKeysLabel(EReference ref) { |
| String result = ""; |
| Collection<String> names = Lists.newArrayList(); |
| for (EAttribute attr : ref.getEKeys()) { |
| if (attr.getName() != null) { |
| names.add(attr.getName()); |
| } |
| } |
| result += Joiner.on(',').join(names); |
| return result; |
| } |
| |
| public EObject getEReferenceTarget(EReference ref) { |
| return EGenericsServices.getETypeOrParameter(ref); |
| } |
| |
| public EObject getEdgeTargetSemantic(EObject any, DEdge view) { |
| return ((DSemanticDecorator) view.getTargetNode()).getTarget(); |
| } |
| |
| public EObject getEdgeSourceSemantic(EObject any, DEdge view) { |
| return ((DSemanticDecorator) view.getSourceNode()).getTarget(); |
| } |
| |
| public List<EReference> getEOppositeEReferences(EPackage context, DSemanticDiagram diagram) { |
| Collection<EClass> eClasses = new DesignServices().getDisplayedEClasses(diagram); |
| Set<EReference> references = Sets.newLinkedHashSet(); |
| for (EClass clazz : eClasses) { |
| references.addAll(clazz.getEReferences()); |
| } |
| Map<String, EReference> map = new HashMap<String, EReference>(); |
| for (EReference ref : references) { |
| if (ref.getEOpposite() != null) { |
| String key1 = ref.getEOpposite().hashCode() + "" + ref.hashCode(); |
| String key2 = ref.hashCode() + "" + ref.getEOpposite().hashCode(); |
| if (map.get(key1) == null && map.get(key2) == null) { |
| map.put(key1, ref); |
| } |
| } |
| } |
| return new ArrayList<EReference>(map.values()); |
| } |
| |
| public String render(EReference ref) { |
| StringBuilder sb = new StringBuilder(); |
| renderCardinality(ref, sb); |
| renderName(ref, sb); |
| return sb.toString(); |
| } |
| |
| public String renderAsNode(EReference ref) { |
| StringBuilder sb = new StringBuilder(); |
| renderName(ref, sb); |
| renderType(ref, sb); |
| return sb.toString(); |
| } |
| |
| private void renderType(EReference ref, StringBuilder sb) { |
| String typeName = EGenericsServices.getETypeLabel(ref); |
| if (typeName != null) { |
| if (ref.getName() != null) { |
| sb.append(" "); |
| } |
| sb.append(TYPE_SEPARATOR).append(" ").append(typeName); |
| } |
| } |
| |
| private void renderCardinality(EReference ref, StringBuilder sb) { |
| sb.append("["); |
| sb.append(renderBound(ref.getLowerBound())); |
| sb.append(CARDINALITY_SEPARATOR); |
| sb.append(renderBound(ref.getUpperBound())); |
| sb.append("]"); |
| } |
| |
| private String renderBound(int bound) { |
| if (bound == -1) { |
| return CARDINALITY_UNBOUNDED; |
| } else { |
| return String.valueOf(bound); |
| } |
| } |
| |
| private void renderName(EReference ref, StringBuilder sb) { |
| if (ref.getName() != null) { |
| sb.append(" "); |
| if (ref.isDerived()) { |
| sb.append(DERIVED_PREFIX); |
| } |
| sb.append(ref.getName()); |
| } |
| } |
| |
| public String superTypesLabel(EClass any) { |
| Collection<String> reifiedTypes = Lists.newArrayList(); |
| Collection<String> typeParameters = Lists.newArrayList(); |
| for (EGenericType genType : any.getEGenericSuperTypes()) { |
| if (genType.getEClassifier() != null) { |
| for (ETypeParameter param : genType.getEClassifier().getETypeParameters()) { |
| if (param.getName() != null) { |
| typeParameters.add(param.getName()); |
| } else { |
| typeParameters.add("?"); |
| } |
| } |
| } |
| for (EGenericType argument : genType.getETypeArguments()) { |
| if (argument.getEClassifier() != null && argument.getEClassifier().getName() != null) { |
| reifiedTypes.add(argument.getEClassifier().getName()); |
| } else if (argument.getETypeParameter() != null && argument.getETypeParameter().getName() != null) { |
| reifiedTypes.add(argument.getETypeParameter().getName()); |
| } else { |
| reifiedTypes.add("?"); |
| } |
| } |
| } |
| if (reifiedTypes.size() > 0) { |
| return "<<bind " + Joiner.on(',').join(typeParameters) + ">> " + Joiner.on(',').join(reifiedTypes); |
| } else { |
| return null; |
| } |
| } |
| |
| public void createTypeArgumentsIfNeeded(EClass host, EClass target) { |
| for (EGenericType genSuperType : host.getEGenericSuperTypes()) { |
| if (genSuperType.getEClassifier() != null) { |
| int parameters = genSuperType.getEClassifier().getETypeParameters().size(); |
| if (genSuperType.getETypeArguments().size() > parameters) { |
| for (int i = genSuperType.getETypeArguments().size(); i > parameters; i--) { |
| genSuperType.getETypeArguments().remove(i); |
| } |
| |
| } else if (genSuperType.getETypeArguments().size() < parameters) { |
| int delta = parameters - genSuperType.getETypeArguments().size(); |
| for (int i = 0; i < delta; i++) { |
| genSuperType.getETypeArguments().add(EcoreFactory.eINSTANCE.createEGenericType()); |
| } |
| } |
| } |
| |
| } |
| } |
| |
| public EReference performEdit(EReference ref, String editString) { |
| if ("0".equals(editString.trim())) { |
| ref.setLowerBound(0); |
| } else if ("1".equals(editString.trim())) { |
| ref.setLowerBound(1); |
| } else if ("11".equals(editString.trim())) { |
| ref.setLowerBound(1); |
| ref.setUpperBound(1); |
| } else if (CARDINALITY_UNBOUNDED.equals(editString.trim())) { |
| ref.setUpperBound(-1); |
| } else if (CARDINALITY_UNBOUNDED_ALTERNATIVE.equals(editString.trim())) { |
| ref.setUpperBound(-1); |
| } else { |
| editName(ref, editString); |
| editCardinality(ref, editString); |
| } |
| return ref; |
| } |
| |
| private void editName(EReference ref, String editString) { |
| String namePart = extractNamePart(ref, editString); |
| if (namePart != null && namePart.trim().length() > 0) { |
| boolean derived = namePart.startsWith(DERIVED_PREFIX); |
| ref.setDerived(derived); |
| ref.setName(namePart.substring(derived ? DERIVED_PREFIX.length() : 0).trim()); |
| } |
| } |
| |
| private String extractNamePart(EReference ref, String name) { |
| int end = name.indexOf("]"); |
| if (end != -1 && end < name.length()) { |
| return name.substring(end + 1).trim(); |
| } else { |
| return name.trim(); |
| } |
| } |
| |
| private void editCardinality(EReference ref, String editString) { |
| List<Integer> card = parseCardinality(editString); |
| if (card.get(0) != null) { |
| ref.setLowerBound(card.get(0).intValue()); |
| } |
| if (card.get(1) != null) { |
| ref.setUpperBound(card.get(1).intValue()); |
| } |
| } |
| |
| /** |
| * Made public only to allow testing. Returns a List instead of an array |
| * because one the method is public (for testing), Acceleo will try to |
| * interpret as a service, but it does not handle arrays. |
| */ |
| public List<Integer> parseCardinality(String editString) { |
| List<Integer> result = Lists.newArrayList(null, null); |
| String spec = extractCardinalityPart(editString); |
| if (spec != null) { |
| if (spec.contains(CARDINALITY_SEPARATOR)) { |
| String[] parts = spec.split(Pattern.quote(CARDINALITY_SEPARATOR)); |
| switch (parts.length) { |
| case 0: |
| break; |
| case 1: |
| if (spec.startsWith(CARDINALITY_SEPARATOR)) { |
| result.set(1, parseBound(parts[0])); |
| } else if (spec.endsWith(CARDINALITY_SEPARATOR)) { |
| result.set(0, parseBound(parts[0])); |
| } |
| break; |
| default: // 2 (or more, but only the first 2 are considered) |
| result.set(0, parseBound(parts[0])); |
| result.set(1, parseBound(parts[1])); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private Integer parseBound(String bound) { |
| if (CARDINALITY_UNBOUNDED.equals(bound.trim())) { |
| return Integer.valueOf(-1); |
| } else { |
| try { |
| return Integer.parseInt(bound.trim()); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * Made public only to allow testing. |
| */ |
| public String extractCardinalityPart(String editString) { |
| int start = editString.indexOf("["); |
| int end = editString.indexOf("]"); |
| if (start != -1 && end != -1 && start < end && end < editString.length()) { |
| return editString.substring(start + 1, end).trim(); |
| } else { |
| return null; |
| } |
| } |
| } |