/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.lang.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Strings;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.core.Flags;
import org.eclipse.xtend.core.xtend.XtendFunction;
import org.eclipse.xtend.core.xtend.XtendMember;
import org.eclipse.xtend.core.xtend.XtendParameter;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmConstructor;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.util.TypeReferences;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.util.XtextVersion;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation;
import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotationElementValuePair;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Inline;
import org.eclipse.xtext.xbase.typesystem.conformance.TypeConformanceComputationArgument;
import org.eclipse.xtext.xbase.typesystem.override.OverrideHelper;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceFactory;
import org.eclipse.xtext.xbase.typesystem.references.StandardTypeReferenceOwner;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
import org.osgi.framework.Version;
import io.sarl.lang.SARLVersion;
import io.sarl.lang.actionprototype.ActionParameterTypes;
import io.sarl.lang.actionprototype.ActionPrototype;
import io.sarl.lang.actionprototype.IActionPrototypeProvider;
import io.sarl.lang.annotation.EarlyExit;
import io.sarl.lang.sarl.SarlAction;
import io.sarl.lang.sarl.SarlFormalParameter;
import io.sarl.lang.services.SARLGrammarKeywordAccess;
/**
* Utilities functions on JvmElements.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:classfanoutcomplexity")
public final class Utils {
/** Character for hidden features.
*/
public static final String HIDDEN_MEMBER_CHARACTER = "$"; //$NON-NLS-1$
private static final String PREFIX_DEFAULT_VALUE = HIDDEN_MEMBER_CHARACTER + "DEFAULT_VALUE" //$NON-NLS-1$
+ HIDDEN_MEMBER_CHARACTER;
private static final String PREFIX_CAPACITY_IMPLEMENTATION = HIDDEN_MEMBER_CHARACTER + "CAPACITY_USE" //$NON-NLS-1$
+ HIDDEN_MEMBER_CHARACTER;
private static final String POSTFIX_CAPACITY_IMPLEMENTATION_CALLER = HIDDEN_MEMBER_CHARACTER + "CALLER"; //$NON-NLS-1$
private static final String PREFIX_GUARD_EVALUATOR = HIDDEN_MEMBER_CHARACTER + "guardEvaluator" //$NON-NLS-1$
+ HIDDEN_MEMBER_CHARACTER;
private static final String PREFIX_GUARD = HIDDEN_MEMBER_CHARACTER + "behaviorUnitGuard" //$NON-NLS-1$
+ HIDDEN_MEMBER_CHARACTER;
private static final String PREFIX_EVENT_HANDLER = HIDDEN_MEMBER_CHARACTER + "behaviorUnit" //$NON-NLS-1$
+ HIDDEN_MEMBER_CHARACTER;
private static final String PREFIX_LOCAL_VARIABLE = "___SARLlocal_"; //$NON-NLS-1$
private static final String HIDDEN_MEMBER_REPLACEMENT_CHARACTER = "_"; //$NON-NLS-1$
private static final String SARL_PACKAGE_PREFIX;
private static final String SARL_VERSION_FIELD_NAME_STR = "SPECIFICATION_RELEASE_VERSION_STRING"; //$NON-NLS-1$
private static final String SARL_VERSION_FIELD_NAME_FLOAT = "SPECIFICATION_RELEASE_VERSION"; //$NON-NLS-1$
private static boolean checkSarlVersionClass = true;
static {
final StringBuilder name = new StringBuilder();
final String[] components = EarlyExit.class.getPackage().getName().split("\\."); //$NON-NLS-1$
final int len = Math.min(3, components.length);
for (int i = 0; i < len; ++i) {
name.append(components[i]);
name.append("."); //$NON-NLS-1$
}
SARL_PACKAGE_PREFIX = name.toString();
}
private Utils() {
//
}
/** Analyzing the type hierarchy of the given interface and
* extract hierarchy information.
*
* @param jvmElement - the element to analyze
* @param operations - filled with the operations inside and inherited by the element.
* @param fields - filled with the fields inside and inherited by the element.
* @param sarlSignatureProvider - provider of tools related to action signatures.
* @see OverrideHelper
*/
public static void populateInterfaceElements(
JvmDeclaredType jvmElement,
Map<ActionPrototype, JvmOperation> operations,
Map<String, JvmField> fields,
IActionPrototypeProvider sarlSignatureProvider) {
for (final JvmFeature feature : jvmElement.getAllFeatures()) {
if (!"java.lang.Object".equals(feature.getDeclaringType().getQualifiedName())) { //$NON-NLS-1$
if (operations != null && feature instanceof JvmOperation) {
final JvmOperation operation = (JvmOperation) feature;
final ActionParameterTypes sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
operation.isVarArgs(), operation.getParameters());
final ActionPrototype actionKey = sarlSignatureProvider.createActionPrototype(
operation.getSimpleName(), sig);
operations.put(actionKey, operation);
} else if (fields != null && feature instanceof JvmField) {
fields.put(feature.getSimpleName(), (JvmField) feature);
}
}
}
}
/** Analyzing the type hierarchy of the given element, and
* extract any type-related information.
*
* @param jvmElement - the element to analyze
* @param finalOperations - filled with the final operations inherited by the element.
* @param overridableOperations - filled with the oervrideable operations inherited by the element.
* @param inheritedFields - filled with the fields inherited by the element.
* @param operationsToImplement - filled with the abstract operations inherited by the element.
* @param superConstructors - filled with the construstors of the super type.
* @param sarlSignatureProvider - provider of tools related to action signatures.
* @see OverrideHelper
*/
@SuppressWarnings({
"checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity",
"checkstyle:nestedifdepth"})
public static void populateInheritanceContext(
JvmDeclaredType jvmElement,
Map<ActionPrototype, JvmOperation> finalOperations,
Map<ActionPrototype, JvmOperation> overridableOperations,
Map<String, JvmField> inheritedFields,
Map<ActionPrototype, JvmOperation> operationsToImplement,
Map<ActionParameterTypes, JvmConstructor> superConstructors,
IActionPrototypeProvider sarlSignatureProvider) {
// Get the operations that must be implemented
if (operationsToImplement != null) {
for (final JvmTypeReference interfaceReference : jvmElement.getExtendedInterfaces()) {
for (final JvmFeature feature : ((JvmGenericType) interfaceReference.getType()).getAllFeatures()) {
if (!"java.lang.Object".equals(//$NON-NLS-1$
feature.getDeclaringType().getQualifiedName())) {
if (feature instanceof JvmOperation) {
final JvmOperation operation = (JvmOperation) feature;
final ActionParameterTypes sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
operation.isVarArgs(), operation.getParameters());
final ActionPrototype actionKey = sarlSignatureProvider.createActionPrototype(
operation.getSimpleName(), sig);
if (operation.isDefault()) {
if (overridableOperations != null) {
overridableOperations.put(actionKey, operation);
}
} else {
if (operationsToImplement != null) {
operationsToImplement.put(actionKey, operation);
}
}
}
}
}
}
}
// Check on the implemented features, inherited from the super type
if (jvmElement.getExtendedClass() != null) {
final JvmGenericType parentType = (JvmGenericType) jvmElement.getExtendedClass().getType();
for (final JvmFeature feature : parentType.getAllFeatures()) {
if (!"java.lang.Object".equals(feature.getDeclaringType().getQualifiedName()) //$NON-NLS-1$
&& isVisible(jvmElement, feature)
&& !isHiddenMember(feature.getSimpleName())) {
if (feature instanceof JvmOperation) {
if (!feature.isStatic()) {
final JvmOperation operation = (JvmOperation) feature;
final ActionParameterTypes sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
operation.isVarArgs(), operation.getParameters());
final ActionPrototype actionKey = sarlSignatureProvider.createActionPrototype(
feature.getSimpleName(), sig);
if (operation.isAbstract() && !operation.isDefault()) {
if (operationsToImplement != null) {
operationsToImplement.put(actionKey, operation);
}
} else if (operation.isFinal()) {
if (finalOperations != null) {
finalOperations.put(actionKey, operation);
}
if (operationsToImplement != null) {
operationsToImplement.remove(actionKey);
}
} else {
if (overridableOperations != null) {
overridableOperations.put(actionKey, operation);
}
if (operationsToImplement != null) {
operationsToImplement.remove(actionKey);
}
}
}
} else if (feature instanceof JvmField && inheritedFields != null) {
inheritedFields.put(feature.getSimpleName(), (JvmField) feature);
}
}
}
if (superConstructors != null) {
for (final JvmConstructor cons : parentType.getDeclaredConstructors()) {
final ActionParameterTypes sig = sarlSignatureProvider.createParameterTypesFromJvmModel(
cons.isVarArgs(), cons.getParameters());
superConstructors.put(sig, cons);
}
}
}
}
/** Replies if the target feature is visible from the type.
*
* @param fromType - the type from which the feature visibility is tested.
* @param target - the feature to test for the visibility.
* @return <code>true</code> if the given type can see the target feature.
*/
public static boolean isVisible(JvmDeclaredType fromType, JvmMember target) {
switch (target.getVisibility()) {
case DEFAULT:
return target.getDeclaringType().getPackageName().equals(fromType.getPackageName());
case PROTECTED:
case PUBLIC:
return true;
case PRIVATE:
default:
}
return false;
}
/** Replies if the last parameter is a variadic parameter.
*
* @param params - parameters.
* @return <code>true</code> if the late parameter is variadic.
*/
public static boolean isVarArg(List<? extends XtendParameter> params) {
assert params != null;
if (params.size() > 0) {
final XtendParameter param = params.get(params.size() - 1);
assert param != null;
return param.isVarArg();
}
return false;
}
/** Replies if the given name is related to an hidden action.
*
* <p>An hidden action is an action that is generated by the SARL
* compiler, and that cannot be defined by the SARL user.
*
* @param name - the name to test.
* @return <code>true</code> if the given name is reserved by SARL.
*/
public static boolean isHiddenMember(String name) {
return name.contains(HIDDEN_MEMBER_CHARACTER);
}
/** Replies a fixed version of the given name assuming
* that it is an hidden action, and reformating
* the reserved text.
*
* <p>An hidden action is an action that is generated by the SARL
* compiler, and that cannot be defined by the SARL user.
*
* @param name - the name to fix.
* @return the fixed name.
*/
public static String fixHiddenMember(String name) {
return name.replaceAll(Pattern.quote(HIDDEN_MEMBER_CHARACTER),
Matcher.quoteReplacement(HIDDEN_MEMBER_REPLACEMENT_CHARACTER));
}
/** Create the name of the hidden attribute that is containing a parameter's default value.
*
* @param id the id of the default value.
* @return the method name.
*/
public static String createNameForHiddenDefaultValueAttribute(String id) {
return PREFIX_DEFAULT_VALUE + fixHiddenMember(id.toUpperCase());
}
/** Create the name of the hidden field that is containing a capacity implementation.
*
* @param id the id of the capacity.
* @return the field name.
*/
public static String createNameForHiddenCapacityImplementationAttribute(String id) {
return PREFIX_CAPACITY_IMPLEMENTATION + fixHiddenMember(id.toUpperCase()).replace(".", //$NON-NLS-1$
HIDDEN_MEMBER_REPLACEMENT_CHARACTER);
}
/** Create the name of the hidden method that is calling a capacity implementation.
*
* @param capacityImplementationFieldName the name of the extension field.
* @return the method name.
*/
public static String createNameForHiddenCapacityImplementationCallingMethodFromFieldName(
String capacityImplementationFieldName) {
return capacityImplementationFieldName + POSTFIX_CAPACITY_IMPLEMENTATION_CALLER;
}
/** Replies if the given simple name is the name of the hidden method that is calling a capacity implementation.
*
* @param simpleName the simple name.
* @return <code>true</code> if the given simple name if for the hidden method for capacuty uses.
*/
public static boolean isNameForHiddenCapacityImplementationCallingMethod(String simpleName) {
return simpleName != null && simpleName.startsWith(PREFIX_CAPACITY_IMPLEMENTATION)
&& simpleName.endsWith(POSTFIX_CAPACITY_IMPLEMENTATION_CALLER);
}
/** Create the name of the hidden local variable.
*
* @param id the name of the local variable.
* @return the variable name.
*/
public static String createNameForHiddenLocalVariable(String id) {
return PREFIX_LOCAL_VARIABLE + fixHiddenMember(id);
}
/** Create the name of the hidden method that is containing the evaluation of all the guards for a given event.
*
* @param eventId the id of the event.
* @return the method name.
*/
public static String createNameForHiddenGuardGeneralEvaluatorMethod(String eventId) {
return PREFIX_GUARD_EVALUATOR + fixHiddenMember(eventId);
}
/** Create the name of the hidden method that is containing the event guard evaluation.
*
* @param eventId the id of the event.
* @param handlerIndex the index of the handler in the container type.
* @return the method name.
*/
public static String createNameForHiddenGuardEvaluatorMethod(String eventId, int handlerIndex) {
return PREFIX_GUARD + fixHiddenMember(eventId)
+ HIDDEN_MEMBER_CHARACTER + handlerIndex;
}
/** Create the name of the hidden method that is containing the event handler code.
*
* @param eventId the id of the event.
* @param handlerIndex the index of the handler in the container type.
* @return the attribute name.
*/
public static String createNameForHiddenEventHandlerMethod(String eventId, int handlerIndex) {
return PREFIX_EVENT_HANDLER + fixHiddenMember(eventId) + HIDDEN_MEMBER_CHARACTER + handlerIndex;
}
/** Replies if the given reference is pointing to a class type.
*
* @param typeRef - the type reference to test.
* @return <code>true</code> if the pointed element is a class type.
*/
public static boolean isClass(LightweightTypeReference typeRef) {
final JvmType t = typeRef.getType();
if (t instanceof JvmGenericType) {
return !((JvmGenericType) t).isInterface();
}
return false;
}
/** Replies if the given type is a class type.
*
* @param type - the type to test.
* @return <code>true</code> if the element is a class type.
*/
public static boolean isClass(Class<?> type) {
return !type.isInterface();
}
/** Replies if the given reference is referencing a final type.
*
* @param expressionTypeRef - the type reference to test.
* @return <code>true</code> if the given type is final.
*/
public static boolean isFinal(LightweightTypeReference expressionTypeRef) {
if (expressionTypeRef.isArray()) {
return isFinal(expressionTypeRef.getComponentType());
}
if (expressionTypeRef.isPrimitive()) {
return true;
}
return expressionTypeRef.getType() instanceof JvmDeclaredType
&& ((JvmDeclaredType) expressionTypeRef.getType()).isFinal();
}
/** Replies if the given type is a final type.
*
* @param expressionType - the type to test.
* @return <code>true</code> if the given type is final.
*/
public static boolean isFinal(Class<?> expressionType) {
if (expressionType.isArray()) {
return isFinal(expressionType.getComponentType());
}
if (expressionType.isPrimitive()) {
return true;
}
return expressionType.isEnum()
|| Modifier.isFinal(expressionType.getModifiers());
}
/** Replies if the given type is an interface.
*
* @param type - the type to test.
* @return <code>true</code> if the given type is an interface.
*/
public static boolean isInterface(LightweightTypeReference type) {
return type.getType() instanceof JvmGenericType
&& ((JvmGenericType) type.getType()).isInterface();
}
/** Replies if it is allowed to cast between the given types.
*
* @param fromType - source type
* @param toType - target type
* @param enablePrimitiveWidening - indicates if the widening of the primitive types is allowed.
* @param enableVoidMatchingNull - indicates if the <code>null</code> is matching <code>void</code>.
* @param allowSynonyms - indicates if the synonyms are allowed.
* @return the state of the cast.
*/
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:booleanexpressioncomplexity"})
public static boolean canCast(
LightweightTypeReference fromType, LightweightTypeReference toType,
boolean enablePrimitiveWidening, boolean enableVoidMatchingNull,
boolean allowSynonyms) {
if (enableVoidMatchingNull) {
final boolean fromVoid = fromType == null || fromType.isPrimitiveVoid();
final boolean toVoid = toType == null || toType.isPrimitiveVoid();
if (fromVoid) {
return toVoid;
}
if (toVoid) {
return fromVoid;
}
assert fromType != null;
assert toType != null;
} else if ((fromType == null || toType == null)
|| (fromType.isPrimitiveVoid() != toType.isPrimitiveVoid())) {
return false;
}
final TypeConformanceComputationArgument conform = new TypeConformanceComputationArgument(
false, false, true, enablePrimitiveWidening, false, allowSynonyms);
if (((fromType.getType() instanceof JvmDeclaredType || fromType.isPrimitive())
// if one of the types is an interface and the other is a non final class
// (or interface) there always can be a subtype
&& (!isInterface(fromType) || isFinal(toType))
&& (!isInterface(toType) || isFinal(fromType))
&& (!toType.isAssignableFrom(fromType, conform))
&& (isFinal(fromType) || isFinal(toType)
|| isClass(fromType) && isClass(toType))
// no upcast
&& (!fromType.isAssignableFrom(toType, conform)))
|| (toType.isPrimitive() && !(fromType.isPrimitive() || fromType.isWrapper()))) {
return false;
}
return true;
}
/** Convert a type reference to a lightweight type reference.
*
* @param typeRef - reference to convert.
* @param services - services used for the conversion
* @return the lightweight type reference.
*/
public static LightweightTypeReference toLightweightTypeReference(
JvmTypeReference typeRef, CommonTypeComputationServices services) {
return toLightweightTypeReference(typeRef, services, false);
}
/** Convert a type reference to a lightweight type reference.
*
* @param typeRef - reference to convert.
* @param services - services used for the conversion
* @param keepUnboundWildcardInformation - indicates if the unbound wild card
* information must be keeped in the lightweight reference.
* @return the lightweight type reference.
*/
public static LightweightTypeReference toLightweightTypeReference(
JvmTypeReference typeRef, CommonTypeComputationServices services,
boolean keepUnboundWildcardInformation) {
if (typeRef == null) {
return null;
}
final StandardTypeReferenceOwner owner = new StandardTypeReferenceOwner(services, typeRef);
final LightweightTypeReferenceFactory factory = new LightweightTypeReferenceFactory(owner,
keepUnboundWildcardInformation);
final LightweightTypeReference reference = factory.toLightweightReference(typeRef);
return reference;
}
/** Convert a type reference to a lightweight type reference.
*
* @param type - type to convert.
* @param services - services used for the conversion
* @return the lightweight type reference.
*/
public static LightweightTypeReference toLightweightTypeReference(
JvmType type, CommonTypeComputationServices services) {
return toLightweightTypeReference(type, services, false);
}
/** Convert a type to a lightweight type reference.
*
* @param type - type to convert.
* @param services - services used for the conversion
* @param keepUnboundWildcardInformation - indicates if the unbound wild card
* information must be keeped in the lightweight reference.
* @return the lightweight type reference.
*/
public static LightweightTypeReference toLightweightTypeReference(
JvmType type, CommonTypeComputationServices services,
boolean keepUnboundWildcardInformation) {
if (type == null) {
return null;
}
final StandardTypeReferenceOwner owner = new StandardTypeReferenceOwner(services, type);
final LightweightTypeReferenceFactory factory = new LightweightTypeReferenceFactory(owner,
keepUnboundWildcardInformation);
final LightweightTypeReference reference = factory.toLightweightReference(type);
return reference;
}
/** Compare the two strings as they are version numbers.
*
* @param v1 - first version to compare.
* @param v2 - second version to compare.
* @return Negative integer of <code>v1</code> is lower than <code>v2</code>;
* positive integer of <code>v1</code> is greater than <code>v2</code>;
* {@code 0} if they are strictly equal.
*/
@Inline(value = "Version.parseVersion($1).compareTo(Version.parseVersion($2))",
imported = {Version.class})
public static int compareVersions(String v1, String v2) {
final Version vobject1 = Version.parseVersion(v1);
final Version vobject2 = Version.parseVersion(v2);
return vobject1.compareTo(vobject2);
}
private static void addAnnotationToSignature(StringBuilder textRepresentation, SARLGrammarKeywordAccess elements,
ISerializer serializer, ImportManager importManager, XAnnotation annotation) {
textRepresentation.append(elements.getCommercialAtKeyword());
textRepresentation.append(getSignatureType(annotation.getAnnotationType(), importManager));
final XExpression value = annotation.getValue();
if (value != null) {
textRepresentation.append(elements.getLeftParenthesisKeyword());
textRepresentation.append(serializer.serialize(value).trim());
textRepresentation.append(elements.getRightParenthesisKeyword());
} else if (!annotation.getElementValuePairs().isEmpty()) {
textRepresentation.append(elements.getLeftParenthesisKeyword());
boolean addComa = false;
for (final XAnnotationElementValuePair pair : annotation.getElementValuePairs()) {
if (addComa) {
textRepresentation.append(elements.getCommaKeyword());
} else {
addComa = true;
}
textRepresentation.append(elements.getEqualsSignKeyword());
textRepresentation.append(serializer.serialize(pair.getValue()).trim());
}
textRepresentation.append(elements.getRightParenthesisKeyword());
}
}
/** This is a context-safe serializer of a signature.
*
* @param signature - the signature to serialize.
* @param serializer - the Xtext serializer
* @param grammarAccess - the accessor to the SARL grammar.
* @param importManager - used to collect the types to import.
* If <code>null</code>, the qualified names of the types with be put in the signature.
* @return the string representation of the signature.
*/
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"})
public static String getActionSignatureString(SarlAction signature, ISerializer serializer,
SARLGrammarKeywordAccess grammarAccess, ImportManager importManager) {
// Try the serializer
try {
//TODO: Check if there is a way to serialize without context
return serializer.serialize(signature);
} catch (Throwable exception) {
// No working, perhaps the context's of the signature is unknown
}
final StringBuilder textRepresentation = new StringBuilder();
// Annotations
for (final XAnnotation annotation : signature.getAnnotations()) {
addAnnotationToSignature(textRepresentation, grammarAccess, serializer, importManager, annotation);
}
// Modifiers
for (final String modifier : signature.getModifiers()) {
textRepresentation.append(modifier);
textRepresentation.append(' ');
}
// Generic type
if (!signature.getTypeParameters().isEmpty()) {
boolean addComa = false;
textRepresentation.append(grammarAccess.getLessThanSignKeyword());
for (final JvmTypeParameter typeParameter : signature.getTypeParameters()) {
if (addComa) {
textRepresentation.append(grammarAccess.getCommaKeyword());
textRepresentation.append(' ');
} else {
addComa = true;
}
textRepresentation.append(getSignatureType(typeParameter, importManager));
}
textRepresentation.append(grammarAccess.getLessThanSignKeyword());
textRepresentation.append(' ');
}
// Name
textRepresentation.append(signature.getName());
// Parameters
if (!signature.getParameters().isEmpty()) {
textRepresentation.append(grammarAccess.getLeftParenthesisKeyword());
final int idx = signature.getParameters().size() - 1;
for (int i = 0; i < idx; ++i) {
addParamToSignature(textRepresentation, signature.getParameters().get(i), grammarAccess,
importManager, serializer);
textRepresentation.append(grammarAccess.getCommaKeyword());
textRepresentation.append(' ');
}
addParamToSignature(textRepresentation, signature.getParameters().get(idx), grammarAccess,
importManager, serializer);
textRepresentation.append(grammarAccess.getRightParenthesisKeyword());
}
// Return type
final JvmTypeReference returnType = signature.getReturnType();
if (returnType != null && !"void".equals(returnType.getIdentifier())) { //$NON-NLS-1$
textRepresentation.append(' ');
textRepresentation.append(grammarAccess.getColonKeyword());
textRepresentation.append(' ');
textRepresentation.append(getSignatureType(returnType.getType(), importManager));
}
// Throws
if (!signature.getExceptions().isEmpty()) {
textRepresentation.append(' ');
textRepresentation.append(grammarAccess.getThrowsKeyword());
textRepresentation.append(' ');
boolean addComa = false;
for (final JvmTypeReference eventType : signature.getExceptions()) {
if (addComa) {
textRepresentation.append(grammarAccess.getCommaKeyword());
textRepresentation.append(' ');
} else {
addComa = true;
}
textRepresentation.append(getSignatureType(eventType.getType(), importManager));
}
}
// Fires
if (!signature.getFiredEvents().isEmpty()) {
textRepresentation.append(' ');
textRepresentation.append(grammarAccess.getFiresKeyword());
textRepresentation.append(' ');
boolean addComa = false;
for (final JvmTypeReference eventType : signature.getFiredEvents()) {
if (addComa) {
textRepresentation.append(grammarAccess.getCommaKeyword());
textRepresentation.append(' ');
} else {
addComa = true;
}
textRepresentation.append(getSignatureType(eventType.getType(), importManager));
}
}
return textRepresentation.toString();
}
private static void addParamToSignature(StringBuilder signature, XtendParameter parameter,
SARLGrammarKeywordAccess grammarAccess, ImportManager importManager, ISerializer serializer) {
signature.append(parameter.getName());
signature.append(' ');
signature.append(grammarAccess.getColonKeyword());
signature.append(' ');
signature.append(getSignatureType(parameter.getParameterType().getType(), importManager));
if (parameter.isVarArg()) {
signature.append(grammarAccess.getWildcardAsteriskKeyword());
} else if (parameter instanceof SarlFormalParameter) {
final SarlFormalParameter sarlParameter = (SarlFormalParameter) parameter;
if (sarlParameter.getDefaultValue() != null) {
signature.append(' ');
signature.append(grammarAccess.getEqualsSignKeyword());
signature.append(' ');
signature.append(serializer.serialize(sarlParameter.getDefaultValue()).trim());
}
}
}
private static String getSignatureType(JvmType type, ImportManager importManager) {
if (importManager != null) {
importManager.addImportFor(type);
return type.getSimpleName();
}
return type.getIdentifier();
}
/** Compute the serial version UID for the given JVM element.
*
* @param jvm - the JVM element.
* @return the serial version UID.
* @see "http://docs.oracle.com/javase/8/docs/platform/serialization/spec/class.html#a4100"
*/
@SuppressWarnings({
"checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity",
"checkstyle:magicnumber",
"checkstyle:booleanexpressioncomplexity"})
public static long computeSerialVersionUID(JvmGenericType jvm) {
final StringBuilder serialVersionUIDBuffer = new StringBuilder();
serialVersionUIDBuffer.append(jvm.getQualifiedName());
BitSet bitset = new BitSet(32);
bitset.set(jvm.getVisibility().getValue());
if (jvm.isFinal()) {
bitset.set(4);
}
if (jvm.isAbstract()) {
bitset.set(5);
}
if (jvm.isInterface()) {
bitset.set(6);
}
serialVersionUIDBuffer.append(bitset.toByteArray());
final SortedSet<JvmTypeReference> superTypes = CollectionLiterals.newTreeSet(new JvmTypeReferenceComparator());
superTypes.addAll(jvm.getSuperTypes());
final SortedSet<JvmField> fields = CollectionLiterals.newTreeSet(new JvmIdentifiableComparator());
final SortedSet<JvmConstructor> constructors = CollectionLiterals.newTreeSet(new JvmIdentifiableComparator());
final SortedSet<JvmOperation> operations = CollectionLiterals.newTreeSet(new JvmIdentifiableComparator());
for (final JvmMember member : jvm.getMembers()) {
if (member instanceof JvmField) {
final JvmField field = (JvmField) member;
if ((field.getVisibility() != JvmVisibility.PRIVATE)
|| (!field.isStatic() && !field.isTransient())) {
fields.add(field);
}
} else if (member instanceof JvmConstructor) {
final JvmConstructor constructor = (JvmConstructor) member;
if (constructor.getVisibility() != JvmVisibility.PRIVATE) {
constructors.add(constructor);
}
} else if (member instanceof JvmOperation) {
final JvmOperation operation = (JvmOperation) member;
if (operation.getVisibility() != JvmVisibility.PRIVATE) {
operations.add(operation);
}
}
}
for (final JvmTypeReference superType : superTypes) {
serialVersionUIDBuffer.append(superType.getQualifiedName());
}
for (final JvmField field : fields) {
serialVersionUIDBuffer.append(field.getSimpleName());
bitset = new BitSet(32);
bitset.set(field.getVisibility().getValue());
if (field.isStatic()) {
bitset.set(4);
}
if (field.isFinal()) {
bitset.set(5);
}
if (field.isVolatile()) {
bitset.set(6);
}
if (field.isTransient()) {
bitset.set(7);
}
serialVersionUIDBuffer.append(bitset.toByteArray());
serialVersionUIDBuffer.append(field.getType().getIdentifier());
}
for (final JvmConstructor constructor : constructors) {
bitset = new BitSet(32);
bitset.set(constructor.getVisibility().getValue());
if (constructor.isStatic()) {
bitset.set(4);
}
if (constructor.isVarArgs()) {
bitset.set(5);
}
serialVersionUIDBuffer.append(bitset.toByteArray());
for (final JvmFormalParameter parameter : constructor.getParameters()) {
serialVersionUIDBuffer.append(parameter.getParameterType().getIdentifier());
}
}
for (final JvmOperation operation : operations) {
bitset = new BitSet(32);
bitset.set(operation.getVisibility().getValue());
if (operation.isStatic()) {
bitset.set(4);
}
if (operation.isFinal()) {
bitset.set(5);
}
if (operation.isSynchronized()) {
bitset.set(6);
}
if (operation.isNative()) {
bitset.set(7);
}
if (operation.isAbstract()) {
bitset.set(8);
}
if (operation.isStrictFloatingPoint()) {
bitset.set(9);
}
if (operation.isVarArgs()) {
bitset.set(10);
}
serialVersionUIDBuffer.append(bitset.toByteArray());
for (final JvmFormalParameter parameter : operation.getParameters()) {
serialVersionUIDBuffer.append(parameter.getParameterType().getIdentifier());
}
}
long key = 1L;
try {
final byte[] uniqueKey = serialVersionUIDBuffer.toString().getBytes();
final byte[] sha = MessageDigest.getInstance("SHA").digest(uniqueKey); //$NON-NLS-1$
key = ((sha[0] >>> 24) & 0xFF)
| ((sha[0] >>> 16) & 0xFF) << 8
| ((sha[0] >>> 8) & 0xFF) << 16
| ((sha[0] >>> 0) & 0xFF) << 24
| ((sha[1] >>> 24) & 0xFF) << 32
| ((sha[1] >>> 16) & 0xFF) << 40
| ((sha[1] >>> 8) & 0xFF) << 48
| ((sha[1] >>> 0) & 0xFF) << 56;
} catch (NoSuchAlgorithmException e) {
//
}
return key;
}
/** Replies a singleton list with the given element, or the empty list if
* the element is <code>null</code>.
*
* @param <T> the type of the element in the list.
* @param element the element.
* @return the list with the element, or the empty list.
*/
public static <T> List<T> singletonList(T element) {
if (element == null) {
return Collections.emptyList();
}
return Collections.singletonList(element);
}
/** Replies the qualified name of the given element.
*
* @param element the element.
* @return the qualified name of the element.
*/
public static QualifiedName getQualifiedName(JvmIdentifiableElement element) {
return QualifiedName.create(element.getQualifiedName('.').split("\\.")); //$NON-NLS-1$
}
/** Replies if the given declaration has an abstract member.
*
* @param declaration - the declaration.
* @return <code>true</code> if the given type has an abstract function.
*/
public static boolean hasAbstractMember(XtendTypeDeclaration declaration) {
if (declaration != null) {
for (final XtendMember member : declaration.getMembers()) {
if (member instanceof XtendFunction) {
if (((XtendFunction) member).isAbstract()) {
return true;
}
}
}
}
return false;
}
/** Check if a compatible SARL library is available on the classpath.
*
* @param typeReferences - the accessor to the types.
* @param context - the context that is providing the access to the classpath.
* @return <code>true</code> if a compatible SARL library was found.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleSARLLibraryOnClasspath(TypeReferences typeReferences, Notifier context) {
final OutParameter<String> version = new OutParameter<>();
final SarlLibraryErrorCode code = getSARLLibraryVersionOnClasspath(typeReferences, context, version);
if (code == SarlLibraryErrorCode.SARL_FOUND) {
return isCompatibleSARLLibraryVersion(version.get());
}
return false;
}
/** Check if a version is compatible with the expected SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if a compatible SARL library was found.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleSARLLibraryVersion(String version) {
return org.eclipse.xtext.util.Strings.equal(SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING, version);
}
/** Check if a version of the JRE is compatible with the SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if this version is for a compatible JRE.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleJREVersion(String version) {
return version != null && !version.isEmpty()
&& compareVersions(version, SARLVersion.MINIMAL_JDK_VERSION) >= 0;
}
/** Check if a version of the current JRE is compatible with the SARL library.
*
* @return <code>true</code> if this version is for a compatible JRE.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleJREVersion() {
return isCompatibleJREVersion(System.getProperty("java.specification.version")); //$NON-NLS-1$
}
/** Check if a version of Xtext is compatible with the SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if this version is for a compatible Xtext.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleXtextVersion(String version) {
return version != null && !version.isEmpty()
&& compareVersions(version, SARLVersion.MINIMAL_XTEXT_VERSION) >= 0;
}
/** Check if a version of the current Xtext is compatible with the SARL library.
*
* @return <code>true</code> if this version is for a compatible Xtext.
* Otherwise <code>false</code>.
*/
public static boolean isCompatibleXtextVersion() {
final XtextVersion xtextVersion = XtextVersion.getCurrent();
if (xtextVersion != null && !Strings.isNullOrEmpty(xtextVersion.getVersion())) {
return isCompatibleXtextVersion(xtextVersion.getVersion());
}
return false;
}
/** Replies the version of the SARL library on the classpath.
*
* @param typeReferences - the accessor to the types.
* @param context - the context that is providing the access to the classpath.
* @return the version, or <code>null</code> if the SARL library cannot be found or
* is too old.
* @deprecated see {@link #getSARLLibraryVersionOnClasspath(TypeReferences, Notifier, OutParameter)}
*/
@Deprecated
public static String getSARLLibraryVersionOnClasspath(TypeReferences typeReferences, Notifier context) {
final OutParameter<String> version = new OutParameter<>();
final SarlLibraryErrorCode code = getSARLLibraryVersionOnClasspath(typeReferences, context, version);
if (code == SarlLibraryErrorCode.SARL_FOUND) {
return version.get();
}
return null;
}
/** Replies the version of the SARL library on the classpath.
*
* @param typeReferences - the accessor to the types.
* @param context the context that is providing the access to the classpath.
* @param version the version of the SARL library that was found, according to the returned error code.
* @return the version, or <code>null</code> if the SARL library cannot be found or
* is too old.
*/
@SuppressWarnings("checkstyle:npathcomplexity")
public static SarlLibraryErrorCode getSARLLibraryVersionOnClasspath(TypeReferences typeReferences, Notifier context,
OutParameter<String> version) {
if (checkSarlVersionClass) {
checkSarlVersionClass = false;
try {
SARLVersion.class.getDeclaredField(SARL_VERSION_FIELD_NAME_STR);
} catch (Exception e) {
try {
SARLVersion.class.getDeclaredField(SARL_VERSION_FIELD_NAME_FLOAT);
} catch (Exception ex) {
return SarlLibraryErrorCode.INVALID_SARL_VERSION_BYTECODE;
}
}
}
final JvmType type;
try {
type = typeReferences.findDeclaredType(SARLVersion.class, context);
} catch (Throwable exception) {
return SarlLibraryErrorCode.NO_SARL_VERSION_CLASS;
}
if (type == null) {
return SarlLibraryErrorCode.NO_SARL_VERSION_CLASS;
}
if (!(type instanceof JvmDeclaredType)) {
return SarlLibraryErrorCode.NO_SARL_VERSION_DECLARED_TYPE;
}
final JvmDeclaredType sarlVersionType = (JvmDeclaredType) type;
JvmField versionField = null;
boolean isString = true;
final Iterator<JvmField> iterator = sarlVersionType.getDeclaredFields().iterator();
while (versionField == null && iterator.hasNext()) {
final JvmField field = iterator.next();
if (SARL_VERSION_FIELD_NAME_STR.equals(field.getSimpleName())) {
versionField = field;
} else if (SARL_VERSION_FIELD_NAME_FLOAT.equals(field.getSimpleName())) {
versionField = field;
isString = false;
}
}
if (versionField == null) {
return SarlLibraryErrorCode.NO_SARL_VERSION_FIELD;
}
final String value;
if (isString) {
value = versionField.getConstantValueAsString();
} else {
final DecimalFormat format = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.US)); //$NON-NLS-1$
value = format.format(versionField.getConstantValueAsFloat());
}
if (Strings.isNullOrEmpty(value)) {
return SarlLibraryErrorCode.NO_SARL_VERSION_VALUE;
}
if (version != null) {
version.set(value);
}
return SarlLibraryErrorCode.SARL_FOUND;
}
/** Replies if the given annotation is an annotation from the SARL core library.
*
* @param type the type of the annotation
* @return <code>true</code> if the given type is a SARL annotation.
*/
public static boolean isSARLAnnotation(Class<?> type) {
return (type != null && Annotation.class.isAssignableFrom(type))
&& isSARLAnnotation(type.getPackage().getName());
}
/** Replies if the given annotation is an annotation from the SARL core library.
*
* @param qualifiedName the qualified name of the annotation type.
* @return <code>true</code> if the given type is a SARL annotation.
*/
public static boolean isSARLAnnotation(String qualifiedName) {
return qualifiedName != null && qualifiedName.startsWith(SARL_PACKAGE_PREFIX);
}
/** Replies if the given type is a primitive "void".
*
* <p>If the given parametr is <code>null</code>, this function returns <code>true</code>.
*
* @param type the type to test.
* @return <code>true</code> if the type is void or <code>null</code>.
*/
public static boolean isPrimitiveVoid(JvmType type) {
// TODO: Is a utility class from Xbase providing this feature that is different from LightweightTypeReference.
return type == null || (type.eClass() == TypesPackage.Literals.JVM_VOID && !type.eIsProxy());
}
/** Replies if the given type is a primitive type.
*
* @param type the type to test.
* @return <code>true</code> if the type is primitive.
*/
public static boolean isPrimitive(JvmType type) {
// TODO: Is a utility class from Xbase providing this feature that is different from LightweightTypeReference.
return type != null && type.eClass() == TypesPackage.Literals.JVM_PRIMITIVE_TYPE;
}
/** Replies if the given type is a functional interface.
*
* <p>This function does not test if the {@code @FunctionalInterface} is attached to the type.
* The function counts the number of operations.
*
* @param type the type to test.
* @param sarlSignatureProvider the provider of SARL operation signatures.
* @return <code>true</code> if the given type is final.
*/
public static boolean isFunctionalInterface(JvmGenericType type, IActionPrototypeProvider sarlSignatureProvider) {
if (type != null && type.isInterface()) {
final Map<ActionPrototype, JvmOperation> operations = new HashMap<>();
populateInterfaceElements(type, operations, null, sarlSignatureProvider);
return operations.size() == 1;
}
return false;
}
/**
* Returns the closest {@link EObject#eContainer() container object} that is not of the requested type.
*
* @param element the element to start from.
* @param type the unexpected type.
* @param container the container.
* @param directContainerChild the child of the container that is or contains the given element.
* @return {@code true} if the container was found.
* @since 0.5
* @see EcoreUtil2#getContainerOfType(EObject, Class)
*/
public static boolean getContainerNotOfType(EObject element, Class<? extends EObject> type,
OutParameter<EObject> container, OutParameter<EObject> directContainerChild) {
EObject previous = element;
EObject elt = element.eContainer();
while (elt != null) {
if (!type.isInstance(elt)) {
if (directContainerChild != null) {
directContainerChild.set(previous);
}
if (container != null) {
container.set(elt);
}
return true;
}
previous = elt;
elt = elt.eContainer();
}
return false;
}
/**
* Returns the closest {@link EObject#eContainer() container object} that is validating the predicate.
*
* @param element the element to start from.
* @param predicate the predicate to test.
* @return the container or {@code null}.
* @since 0.5
* @see EcoreUtil2#getContainerOfType(EObject, Class)
*/
public static EObject getFirstContainerNotOfType(EObject element, Function1<EObject, Boolean> predicate) {
if (predicate == null || element == null) {
return null;
}
EObject elt = element.eContainer();
while (elt != null) {
if (predicate.apply(elt)) {
return elt;
}
elt = elt.eContainer();
}
return null;
}
/** Error code for the
* {@link Utils#getSARLLibraryVersionOnClasspath(TypeReferences, Notifier, OutParameter)}
* function.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.5
*/
public enum SarlLibraryErrorCode {
/** SARL Library was found.
*/
SARL_FOUND,
/** SARL version class not found.
*/
NO_SARL_VERSION_CLASS,
/** SARL version class is not a Xtext declared type.
*/
NO_SARL_VERSION_DECLARED_TYPE,
/** SARL version field not found.
*/
NO_SARL_VERSION_FIELD,
/** SARL version value not found.
*/
NO_SARL_VERSION_VALUE,
/** The byte code (the class) of {@link SARLVersion} does not contains the expected field.
*/
INVALID_SARL_VERSION_BYTECODE,
}
/** Dump the object.
*
* @param object the object.
* @param includeStaticField indicates if the static fields should be included.
* @return the string representation of the object.
* @since 0.6
*/
@SuppressWarnings("checkstyle:npathcomplexity")
public static String dump(Object object, boolean includeStaticField) {
if (object == null) {
return new String();
}
final StringBuilder buffer = new StringBuilder();
final LinkedList<Class<?>> types = new LinkedList<>();
types.add(object.getClass());
while (!types.isEmpty()) {
final Class<?> type = types.removeFirst();
final Class<?> supertype = type.getSuperclass();
if (supertype != null && !supertype.equals(Object.class)) {
types.add(supertype);
}
if (buffer.length() > 0) {
buffer.append("\n"); //$NON-NLS-1$
}
final Field[] fields = type.getDeclaredFields();
buffer.append(type.getSimpleName()).append(" {\n"); //$NON-NLS-1$
boolean firstRound = true;
for (final Field field : fields) {
if (!includeStaticField && Flags.isStatic(field.getModifiers())) {
continue;
}
if (!firstRound) {
buffer.append(",\n"); //$NON-NLS-1$
}
firstRound = false;
field.setAccessible(true);
try {
final Object fieldObj = field.get(object);
final String value;
if (null == fieldObj) {
value = "null"; //$NON-NLS-1$
} else {
value = fieldObj.toString();
}
buffer.append('\t').append(field.getName()).append('=').append('"');
buffer.append(org.eclipse.xtext.util.Strings.convertToJavaString(value));
buffer.append("\"\n"); //$NON-NLS-1$
} catch (IllegalAccessException ignore) {
//this should never happen
}
}
buffer.append('}');
}
return buffer.toString();
}
}