blob: 219176113e78522b4c77f968785c25c1a60cc752 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}