| /******************************************************************************* |
| * Copyright (c) 2015 Obeo. |
| * 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.acceleo.query.services; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.acceleo.annotations.api.documentation.Documentation; |
| import org.eclipse.acceleo.annotations.api.documentation.Example; |
| import org.eclipse.acceleo.annotations.api.documentation.Other; |
| import org.eclipse.acceleo.annotations.api.documentation.Param; |
| import org.eclipse.acceleo.annotations.api.documentation.ServiceProvider; |
| import org.eclipse.acceleo.query.ast.Call; |
| import org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment; |
| import org.eclipse.acceleo.query.runtime.IService; |
| import org.eclipse.acceleo.query.runtime.IValidationResult; |
| import org.eclipse.acceleo.query.runtime.impl.AbstractServiceProvider; |
| import org.eclipse.acceleo.query.runtime.impl.JavaMethodService; |
| import org.eclipse.acceleo.query.runtime.impl.Nothing; |
| import org.eclipse.acceleo.query.runtime.impl.ValidationServices; |
| import org.eclipse.acceleo.query.validation.type.ClassType; |
| import org.eclipse.acceleo.query.validation.type.EClassifierType; |
| import org.eclipse.acceleo.query.validation.type.IType; |
| import org.eclipse.emf.common.util.Enumerator; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EEnum; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| |
| //@formatter:off |
| @ServiceProvider( |
| value = "Services available for all types" |
| ) |
| //@formatter:on |
| @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype" }) |
| public class AnyServices extends AbstractServiceProvider { |
| |
| /** |
| * Line separator constant. |
| */ |
| private static final String LINE_SEP = System.getProperty("line.separator"); |
| |
| /** |
| * The {@link IReadOnlyQueryEnvironment}. |
| */ |
| private final IReadOnlyQueryEnvironment queryEnvironment; |
| |
| /** |
| * Constructor. |
| * |
| * @param queryEnvironment |
| * the {@link IReadOnlyQueryEnvironment} |
| */ |
| public AnyServices(IReadOnlyQueryEnvironment queryEnvironment) { |
| this.queryEnvironment = queryEnvironment; |
| } |
| |
| @Override |
| protected IService getService(Method publicMethod) { |
| final IService result; |
| |
| if ("oclAsType".equals(publicMethod.getName())) { |
| result = new OCLAsTypeService(publicMethod, this); |
| } else { |
| result = new JavaMethodService(publicMethod, this); |
| } |
| |
| return result; |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Indicates whether the object \"o1\" i\"the same as the object \"o2\". For more " + |
| "information refer to the Object#equals(Object) method.", |
| params = { |
| @Param(name = "o1", value = "The object to compare for equality"), |
| @Param(name = "o2", value = "The reference object with which to compare") |
| }, |
| result = "true\" if the object \"o1\" is the same as the object \"o2\", " + |
| "\"false\" otherwise", |
| examples = { |
| @Example(expression = "'Hello'.equals('World')", result = "false"), |
| @Example(expression = "'Hello'.equals('Hello')", result = "true") |
| } |
| ) |
| // @formatter:on |
| public Boolean equals(Object o1, Object o2) { |
| final boolean result; |
| |
| if (o1 == null) { |
| result = o2 == null; |
| } else { |
| result = o1.equals(o2); |
| } |
| |
| return Boolean.valueOf(result); |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Indicates whether the object \"o1\" is a different object from the object \"o2\".", |
| params = { |
| @Param(name = "o1", value = "The object to compare"), |
| @Param(name = "o2", value = "The reference object with which to compare") |
| }, |
| result = "\"true\" if the object \"o1\" is not the same as the object \"o2\", " + |
| "\"false\" otherwise.", |
| examples = { |
| @Example(expression = "'Hello'.differs('World')", result = "true"), |
| @Example(expression = "'Hello'.differs('Hello')", result = "false") |
| } |
| ) |
| // @formatter:on |
| public Boolean differs(Object o1, Object o2) { |
| return Boolean.valueOf(!equals(o1, o2)); |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Returns the concatenation of self (as a String) and the given string \"s\".", |
| params = { |
| @Param(name = "self", value = "The current object at the end of which to append \"s\"."), |
| @Param(name = "s", value = "The string we want to append at the end of the current object's string representation.") |
| }, |
| result = "The string representation of self for which we added the string \"s\".", |
| examples = { |
| @Example(expression = "42.add(' times')", result = "'42 times'") |
| } |
| ) |
| // @formatter:on |
| public String add(Object self, String s) { |
| final String result; |
| |
| if (s == null) { |
| result = toString(self); |
| } else { |
| result = toString(self) + s; |
| } |
| |
| return result; |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Returns the concatenation of the current string and the given object \"any\" (as a String).", |
| params = { |
| @Param(name = "self", value = "The current string."), |
| @Param(name = "any", value = "The object we want to append, as a string, at the end of the current string.") |
| }, |
| result = "The current string with the object \"any\" appended (as a String).", |
| examples = { |
| @Example(expression = "'times '.add(42)", result = "'times 42'") |
| } |
| ) |
| // @formatter:on |
| public String add(String self, Object any) { |
| final String result; |
| |
| if (self == null) { |
| result = toString(any); |
| } else { |
| result = self + toString(any); |
| } |
| |
| return result; |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Casts the current object to the given type.", |
| params = { |
| @Param(name = "object", value = "The object to cast"), |
| @Param(name = "type", value = "The type to cast the object to") |
| }, |
| result = "The current object cast to a \"type\"", |
| examples = { |
| @Example( |
| expression = "anEPackage.oclAsType(ecore::EPackage)", result = "anEPackage", |
| others = { |
| @Other( |
| language = Other.ACCELEO_3, expression = "anEPackage.oclAsType(ecore::EPackage)", result = "anEPackage" |
| ) |
| } |
| ), |
| @Example( |
| expression = "anEPackage.oclAsType(ecore::EClass)", result = "anEPackage", |
| others = { |
| @Other( |
| language = Other.ACCELEO_3, expression = "anEPackage.oclAsType(ecore::EClass)", result = "oclInvalid" |
| ) |
| } |
| ), |
| }, |
| comment = "Contrary to Acceleo 3, the type is ignored, the given object will be returned directly." |
| ) |
| // @formatter:on |
| public Object oclAsType(Object object, Object type) { |
| if (oclIsKindOf(object, type)) { |
| return object; |
| } |
| throw new ClassCastException(object + " cannot be cast to " + type); |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Evaluates to \"true\" if the type of the object o1 conforms to the type \"classifier\". That is, " + |
| "o1 is of type \"classifier\" or a subtype of \"classifier\".", |
| params = { |
| @Param(name = "object", value = "The reference Object we seek to test."), |
| @Param(name = "type", value = "The expected supertype classifier.") |
| }, |
| result = "\"true\" if the object o1 is a kind of the classifier, \"false\" otherwise.", |
| examples = { |
| @Example(expression = "anEPackage.oclIsKindOf(ecore::EPackage)", result = "true"), |
| @Example(expression = "anEPackage.oclIsKindOf(ecore::ENamedElement)", result = "true") |
| } |
| ) |
| // @formatter:on |
| public Boolean oclIsKindOf(Object object, Object type) { |
| Boolean result; |
| if (object == null && type != null) { |
| // OCL considers "null" (OclVoid) to be compatible with everything. |
| // AQL considers it incompatible with anything. |
| result = false; |
| } else if (type instanceof EClass) { |
| EClass eClass = (EClass)type; |
| if (object instanceof EObject) { |
| result = eClass.isInstance(object); |
| } else { |
| result = false; |
| } |
| } else if (type instanceof EEnum) { |
| if (object instanceof EEnumLiteral) { |
| result = ((EEnumLiteral)object).getEEnum().equals(type); |
| } else if (object instanceof Enumerator) { |
| EEnumLiteral literal = ((EEnum)type).getEEnumLiteral(((Enumerator)object).getName()); |
| result = literal.getEEnum().equals(type); |
| } else { |
| result = false; |
| } |
| } else if (type instanceof EDataType) { |
| result = ((EDataType)type).isInstance(object); |
| } else if (object != null && type instanceof Class<?>) { |
| result = ((Class<?>)type).isInstance(object); |
| } else { |
| result = false; |
| } |
| return result; |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Evaluates to \"true\" if the object o1 if of the type \"classifier\" but not a subtype of the " + |
| "\"classifier\".", |
| params = { |
| @Param(name = "object", value = "The reference Object we seek to test."), |
| @Param(name = "type", value = "The expected type classifier.") |
| }, |
| result = "\"true\" if the object o1 is a type of the classifier, \"false\" otherwise.", |
| examples = { |
| @Example(expression = "anEPackage.oclIsKindOf(ecore::EPackage)", result = "true"), |
| @Example(expression = "anEPackage.oclIsKindOf(ecore::ENamedElement)", result = "false") |
| } |
| ) |
| // @formatter:on |
| public Boolean oclIsTypeOf(Object object, Object type) { |
| Boolean result; |
| if (object == null && type != null) { |
| // OCL considers "null" (OclVoid) to be compatible with everything. |
| // AQL considers it incompatible with anything. |
| result = false; |
| } else if (type instanceof EClass) { |
| EClass eClass = (EClass)type; |
| if (object instanceof EObject) { |
| result = eClass == ((EObject)object).eClass(); |
| } else { |
| result = false; |
| } |
| } else if (type instanceof EEnum) { |
| if (object instanceof EEnumLiteral) { |
| result = ((EEnumLiteral)object).getEEnum().equals(type); |
| } else if (object instanceof Enumerator) { |
| EEnumLiteral literal = ((EEnum)type).getEEnumLiteral(((Enumerator)object).getName()); |
| result = literal.getEEnum().equals(type); |
| } else { |
| result = false; |
| } |
| } else if (type instanceof EDataType) { |
| result = ((EDataType)type).isInstance(object); |
| } else if (object != null && type instanceof Class<?>) { |
| result = ((Class<?>)type).equals(object.getClass()); |
| } else { |
| result = false; |
| } |
| return result; |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Returns a string representation of the current object.", |
| params = { |
| @Param(name = "self", value = "The current object") |
| }, |
| result = "a String representation of the given Object. For Collections, this will be the concatenation of " + |
| "all contained Objects' toString.", |
| examples = { |
| @Example(expression = "42.toString()", result = "'42'") |
| } |
| ) |
| // @formatter:on |
| public String toString(Object object) { |
| final StringBuffer buffer = new StringBuffer(); |
| if (object instanceof Collection<?>) { |
| final Iterator<?> childrenIterator = ((Collection<?>)object).iterator(); |
| while (childrenIterator.hasNext()) { |
| buffer.append(toString(childrenIterator.next())); |
| } |
| } else if (object != null && !(object instanceof Nothing)) { |
| final String toString = object.toString(); |
| if (toString != null) { |
| buffer.append(toString); |
| } |
| } |
| // else return empty String |
| return buffer.toString(); |
| } |
| |
| // @formatter:off |
| @Documentation( |
| value = "Returns a string representation of the current environment.", |
| params = { |
| @Param(name = "self", value = "The current object") |
| }, |
| result = "a string representation of the current environment.", |
| examples = { |
| @Example(expression = "42.trace()", result = "'Metamodels:\n\thttp://www.eclipse.org/emf/2002/Ecore\n" + |
| "Services:\n\torg.eclipse.acceleo.query.services.AnyServices\n\t\tpublic java.lang.String org." + |
| "eclipse.acceleo.query.services.AnyServices.add(java.lang.Object,java.lang.String)\n\t\t...\nreceiver: 42\n'") |
| } |
| ) |
| // @formatter:on |
| public String trace(Object object) { |
| final StringBuilder result = new StringBuilder(); |
| |
| result.append("Metamodels:" + LINE_SEP); |
| for (EPackage ePgk : queryEnvironment.getEPackageProvider().getRegisteredEPackages()) { |
| result.append("\t" + ePgk.getNsURI() + LINE_SEP); |
| } |
| result.append("Services:" + LINE_SEP); |
| final List<IService> services = new ArrayList<IService>(queryEnvironment.getLookupEngine() |
| .getRegisteredServices()); |
| Collections.sort(services, new Comparator<IService>() { |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) |
| */ |
| @Override |
| public int compare(IService service1, IService service2) { |
| final int result; |
| |
| if (service1.getPriority() < service2.getPriority()) { |
| result = -1; |
| } else if (service1.getPriority() < service2.getPriority()) { |
| result = 1; |
| } else { |
| result = service1.getName().compareTo(service2.getName()); |
| } |
| return result; |
| } |
| |
| }); |
| for (IService service : services) { |
| result.append("\t\t" + service.getLongSignature() + LINE_SEP); |
| } |
| result.append("receiver: "); |
| result.append(toString(object) + LINE_SEP); |
| |
| return result.toString(); |
| } |
| |
| private static class OCLAsTypeService extends FilterService { |
| public OCLAsTypeService(Method publicMethod, Object serviceInstance) { |
| super(publicMethod, serviceInstance); |
| } |
| |
| @Override |
| public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult, |
| IReadOnlyQueryEnvironment environment, List<IType> argTypes) { |
| final Set<IType> result = new LinkedHashSet<IType>(); |
| |
| final IType receiverType = argTypes.get(0); |
| final IType filterType = argTypes.get(1); |
| if (services.lower(receiverType, filterType) != null) { |
| Object resultType = filterType.getType(); |
| if (resultType instanceof EClassifier) { |
| result.add(new EClassifierType(environment, (EClassifier)resultType)); |
| } else if (resultType instanceof Class) { |
| result.add(new ClassType(environment, (Class<?>)resultType)); |
| } else if (resultType != null) { |
| result.add(services.nothing("Unknown type %s", resultType)); |
| } else { |
| result.add(services.nothing("Unknown type %s", "null")); |
| } |
| } else { |
| if (receiverType instanceof EClassifierType |
| && !environment.getEPackageProvider().isRegistered( |
| ((EClassifierType)receiverType).getType())) { |
| result.add(services.nothing("%s is not registered within the current environment.", |
| receiverType)); |
| } else if (filterType instanceof EClassifierType |
| && !environment.getEPackageProvider().isRegistered( |
| ((EClassifierType)filterType).getType())) { |
| result.add(services.nothing("%s is not registered within the current environment.", |
| filterType)); |
| } else { |
| result.add(services.nothing("%s is not compatible with %s", receiverType, filterType)); |
| } |
| } |
| |
| return result; |
| } |
| } |
| } |