| /******************************************************************************* |
| * 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.Collections; |
| import java.util.List; |
| |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EParameter; |
| import org.eclipse.emf.ecore.ETypedElement; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Ordering; |
| |
| /** |
| * Services dealing with EOperations usable from a VSM. |
| */ |
| public class EOperationServices { |
| private static final String TYPE_SEPARATOR = ":"; |
| |
| private static final String PARAMETERS_SEPARATOR = ","; |
| |
| /** |
| * Computes the label of an EOperation. |
| */ |
| public String render(EOperation op) { |
| StringBuilder sb = new StringBuilder(); |
| renderName(op, sb); |
| renderParameters(op, sb, true); |
| renderType(op, sb); |
| return sb.toString(); |
| } |
| |
| /** |
| * Computes the tooltip of an EOperation (same as label but excluding the |
| * parameters Types) . |
| * |
| * @param op |
| * the operation to be rendered |
| * @return the tooltip of the given EOperation (same as label but excluding |
| * the parameters Types) |
| */ |
| public String renderEOperationTooltip(EOperation op) { |
| StringBuilder sb = new StringBuilder(); |
| renderName(op, sb); |
| renderParameters(op, sb, false); |
| renderType(op, sb); |
| return sb.toString(); |
| } |
| |
| /** |
| * Performs a "direct edit" operation on an EOperation. |
| */ |
| public EOperation performEdit(EOperation op, String editString) { |
| editName(op, editString); |
| editParameters(op, editString); |
| editType(op, editString); |
| return op; |
| } |
| |
| private void renderName(EOperation op, StringBuilder sb) { |
| if (op.getName() != null) { |
| sb.append(op.getName()); |
| } |
| } |
| |
| /** |
| * Renders the parameters of the given EOperation. |
| * |
| * @param op |
| * the operation to render |
| * @param sb |
| * the string builder used to render the eOperation |
| * @param includeParameterType |
| * indicates whether parameter's Types should be rendered or not |
| */ |
| private void renderParameters(EOperation op, StringBuilder sb, |
| boolean includeParameterType) { |
| sb.append("("); |
| boolean first = true; |
| for (EParameter param : op.getEParameters()) { |
| if (!first) { |
| sb.append(PARAMETERS_SEPARATOR).append(" "); |
| } else { |
| first = false; |
| } |
| sb.append(renderParameter(param, includeParameterType)); |
| } |
| sb.append(")"); |
| } |
| |
| /** |
| * Renders the given Parameter. |
| * |
| * @param parameter |
| * the parameter to render |
| * @param includeParameterType |
| * indicates whether parameter's Type should be rendered or not |
| */ |
| private String renderParameter(EParameter parameter, |
| boolean includeParameterType) { |
| String name = parameter.getName(); |
| String typeName = EGenericsServices.getETypeLabel(parameter); |
| if (name != null && typeName != null && includeParameterType) { |
| return parameter.getName() + " " + typeName; |
| } else if (name != null) { |
| return parameter.getName(); |
| } else { |
| return ""; |
| } |
| } |
| |
| private void renderType(EOperation op, StringBuilder sb) { |
| String typeLabel = EGenericsServices.getETypeLabel(op); |
| if (typeLabel != null) { |
| sb.append(" ").append(TYPE_SEPARATOR).append(" "); |
| sb.append(typeLabel); |
| } |
| } |
| |
| private void editName(EOperation op, String editString) { |
| int nameEnd = editString.length(); |
| int typeStart = editString.indexOf(TYPE_SEPARATOR); |
| if (typeStart != -1) { |
| nameEnd = Math.min(nameEnd, typeStart); |
| } |
| int paramStart = editString.indexOf("("); |
| if (paramStart != -1) { |
| nameEnd = Math.min(nameEnd, paramStart); |
| } |
| |
| String namePart = editString.substring(0, nameEnd); |
| if (namePart.trim().length() > 0) { |
| op.setName(namePart.trim()); |
| } else if (typeStart == -1 && paramStart == -1) { |
| // Reset the name only if no other parts have been specified. |
| op.setName(null); |
| } |
| } |
| |
| private void editParameters(EOperation op, String editString) { |
| int paramStart = editString.indexOf("("); |
| int paramEnd = editString.lastIndexOf(")"); |
| if (paramStart != -1 && paramEnd != -1 && paramStart < paramEnd) { |
| List<String[]> spec = parseParameters(editString.substring( |
| paramStart + 1, paramEnd)); |
| int nbParam = spec.size(); |
| // Remove parameters from the end if we have too many. |
| for (int i = op.getEParameters().size() - 1; i >= nbParam; i--) { |
| op.getEParameters().remove(i); |
| } |
| // Update existing parameters with new spec and append additional |
| // ones if necessary |
| for (int i = 0; i < nbParam; i++) { |
| EParameter param = null; |
| if (i < op.getEParameters().size()) { |
| param = op.getEParameters().get(i); |
| } else { |
| param = EcoreFactory.eINSTANCE.createEParameter(); |
| op.getEParameters().add(param); |
| } |
| updateParameter(op, param, spec.get(i)); |
| } |
| } |
| } |
| |
| private void updateParameter(EOperation op, EParameter param, |
| String[] paramSpec) { |
| if (paramSpec[0] != null) { |
| param.setName(paramSpec[0]); |
| } |
| if (paramSpec[1] != null) { |
| EClassifier type = new DesignServices().findTypeByName(op, |
| paramSpec[1]); |
| if (type != null) { |
| param.setEType(type); |
| } |
| } |
| } |
| |
| private List<String[]> parseParameters(String spec) { |
| List<String[]> result = new ArrayList<String[]>(); |
| if (spec.contains(PARAMETERS_SEPARATOR)) { |
| String[] params = spec.split(PARAMETERS_SEPARATOR); |
| for (String paramSpec : params) { |
| result.add(parseParameter(paramSpec)); |
| } |
| } else if (spec.trim().length() > 0) { |
| result.add(parseParameter(spec)); |
| } |
| return result; |
| } |
| |
| /** |
| * Parses a single parameter specification. Supports the following syntaxes: |
| * |
| * <pre> |
| * "name" |
| * "name Type" |
| * "name : Type" |
| * " : Type" |
| * </pre> |
| * |
| * Returns an array of two strings, representing the parameter name and the |
| * type name. Both can be <code>null</code> if the corresponding part is not |
| * specified. |
| * |
| * Note: this method is public only so that it can be visible from tests. |
| */ |
| public String[] parseParameter(String paramSpec) { |
| String[] type = null; |
| // Try ":" as type separator |
| int typeStart = paramSpec.indexOf(TYPE_SEPARATOR); |
| if (typeStart != -1) { |
| type = splitParameterSpec(paramSpec, typeStart); |
| } else { |
| // If there is no ":", assume whitespace separates the name from the |
| // type |
| paramSpec = paramSpec.trim(); |
| typeStart = paramSpec.lastIndexOf(" "); |
| if (typeStart != -1) { |
| type = splitParameterSpec(paramSpec, typeStart); |
| } else { |
| typeStart = paramSpec.lastIndexOf("\t"); |
| if (typeStart != -1) { |
| type = splitParameterSpec(paramSpec, typeStart); |
| } |
| } |
| } |
| // If there was neither ":" nor space, the spec can specify only the |
| // name, or maybe nothing if it is blank. |
| if (type == null) { |
| String name = paramSpec.trim(); |
| if (name.length() == 0) { |
| name = null; |
| } |
| type = new String[] { name, null }; |
| } |
| return type; |
| } |
| |
| public List<ENamedElement> getAllAssociatedElements(EOperation op) { |
| // [eParameters->including(self)->asSequence()->sortedBy(name)/] |
| List<ENamedElement> result = Lists.newArrayListWithExpectedSize(1 + op |
| .getEParameters().size()); |
| result.add(op); |
| result.addAll(op.getEParameters()); |
| Collections.sort( |
| result, |
| Ordering.natural().onResultOf( |
| new Function<ENamedElement, String>() { |
| public String apply(ENamedElement input) { |
| return input.getName(); |
| } |
| })); |
| return result; |
| } |
| |
| private String[] splitParameterSpec(String paramSpec, int typeStart) { |
| String[] type; |
| String name; |
| String typeName; |
| name = paramSpec.substring(0, typeStart).trim(); |
| if (typeStart != paramSpec.length() - 1) { |
| typeName = paramSpec.substring(typeStart + 1).trim(); |
| } else { |
| typeName = null; |
| } |
| if (name.length() == 0) { |
| name = null; |
| } |
| if (typeName != null && typeName.length() == 0) { |
| typeName = null; |
| } |
| type = new String[] { name, typeName }; |
| return type; |
| } |
| |
| private void editType(EOperation op, String editString) { |
| Object value = getSpecifiedType(op, editString); |
| if (value != null) { |
| EGenericsServices.setETypeWithGenerics(op, value); |
| } else { |
| // Only reset the type to null if the ":" is explicitly present with |
| // no type specified |
| int typeStart = editString.lastIndexOf(TYPE_SEPARATOR); |
| if (typeStart != -1 |
| && !editString.substring(typeStart).contains(")")) { |
| op.setEType(null); |
| } |
| } |
| } |
| |
| private Object getSpecifiedType(ETypedElement receiver, String editString) { |
| int typeStart = editString.lastIndexOf(TYPE_SEPARATOR); |
| if (typeStart != -1 && editString.length() > typeStart + 1) { |
| String typeName = editString.substring(typeStart + 1).trim(); |
| if (!typeName.contains(")")) { |
| Object value = EGenericsServices.findGenericType(receiver, |
| typeName); |
| if (value == null) { |
| value = new DesignServices().findTypeByName(receiver, |
| typeName); |
| } |
| return value; |
| } |
| } |
| return null; |
| } |
| } |