blob: 7922bb2ef7fff84d44bf552b94d12123ab450db7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2017 Ecliptical Software Inc. 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:
* Ecliptical Software Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.ds.internal.annotations;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Pattern;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.compiler.BuildContext;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.core.ModelChangedEvent;
import org.eclipse.pde.internal.core.project.PDEProject;
import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
import org.eclipse.pde.internal.core.text.IDocumentElementNode;
import org.eclipse.pde.internal.core.text.IDocumentObject;
import org.eclipse.pde.internal.core.text.IDocumentTextNode;
import org.eclipse.pde.internal.core.text.IModelTextChangeListener;
import org.eclipse.pde.internal.ds.core.IDSComponent;
import org.eclipse.pde.internal.ds.core.IDSConstants;
import org.eclipse.pde.internal.ds.core.IDSDocumentFactory;
import org.eclipse.pde.internal.ds.core.IDSImplementation;
import org.eclipse.pde.internal.ds.core.IDSModel;
import org.eclipse.pde.internal.ds.core.IDSObject;
import org.eclipse.pde.internal.ds.core.IDSProperties;
import org.eclipse.pde.internal.ds.core.IDSProperty;
import org.eclipse.pde.internal.ds.core.IDSProvide;
import org.eclipse.pde.internal.ds.core.IDSReference;
import org.eclipse.pde.internal.ds.core.IDSService;
import org.eclipse.pde.internal.ds.core.text.DSModel;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
@SuppressWarnings("restriction")
public class AnnotationProcessor extends ASTRequestor {
private static final String DS_BUILDER = "org.eclipse.pde.ds.core.builder"; //$NON-NLS-1$
static final Debug debug = Debug.getDebug("ds-annotation-builder/processor"); //$NON-NLS-1$
private final ProjectContext context;
private final Map<ICompilationUnit, BuildContext> fileMap;
private boolean hasBuilder;
public AnnotationProcessor(ProjectContext context, Map<ICompilationUnit, BuildContext> fileMap) {
this.context = context;
this.fileMap = fileMap;
}
static String getCompilationUnitKey(ICompilationUnit source) {
IJavaElement parent = source.getParent();
if (parent == null)
return source.getElementName();
else
return String.format("%s/%s", parent.getElementName().replace('.', '/'), source.getElementName()); //$NON-NLS-1$
}
@Override
public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
// determine CU key
String cuKey = getCompilationUnitKey(source);
context.getUnprocessed().remove(cuKey);
ProjectState state = context.getState();
HashMap<String, String> dsKeys = new HashMap<>();
HashSet<DSAnnotationProblem> problems = new HashSet<>();
ast.accept(new AnnotationVisitor(this, state, dsKeys, problems));
// track abandoned files (may be garbage)
Collection<String> oldDSKeys = state.updateMappings(cuKey, dsKeys);
if (oldDSKeys != null) {
oldDSKeys.removeAll(dsKeys.values());
context.getAbandoned().addAll(oldDSKeys);
}
if (!problems.isEmpty()) {
char[] filename = source.getResource().getFullPath().toString().toCharArray();
for (DSAnnotationProblem problem : problems) {
problem.setOriginatingFileName(filename);
if (problem.getSourceStart() >= 0)
problem.setSourceLineNumber(ast.getLineNumber(problem.getSourceStart()));
}
BuildContext buildContext = fileMap.get(source);
if (buildContext != null)
buildContext.recordNewProblems(problems.toArray(new CategorizedProblem[problems.size()]));
}
}
private void ensureDSProject(IProject project) throws CoreException {
IProjectDescription description = project.getDescription();
ICommand[] commands = description.getBuildSpec();
for (ICommand command : commands) {
if (DS_BUILDER.equals(command.getBuilderName()))
return;
}
ICommand[] newCommands = new ICommand[commands.length + 1];
System.arraycopy(commands, 0, newCommands, 0, commands.length);
ICommand command = description.newCommand();
command.setBuilderName(DS_BUILDER);
newCommands[newCommands.length - 1] = command;
description.setBuildSpec(newCommands);
project.setDescription(description, null);
}
private void ensureExists(IFolder folder) throws CoreException {
if (folder.exists())
return;
IContainer parent = folder.getParent();
if (parent != null && parent.getType() == IResource.FOLDER)
ensureExists((IFolder) parent);
folder.create(true, true, null);
}
void verifyOutputLocation(IFile file) throws CoreException {
if (hasBuilder)
return;
hasBuilder = true;
IProject project = file.getProject();
IPath parentPath = file.getParent().getProjectRelativePath();
if (!parentPath.isEmpty()) {
IFolder folder = project.getFolder(parentPath);
ensureExists(folder);
}
try {
ensureDSProject(project);
} catch (CoreException e) {
Activator.log(e);
}
}
}
@SuppressWarnings("restriction")
class AnnotationVisitor extends ASTVisitor {
private static final String COMPONENT_CONTEXT = "org.osgi.service.component.ComponentContext"; //$NON-NLS-1$
private static final String COMPONENT_SERVICE_OBJECTS = "org.osgi.service.component.ComponentServiceObjects"; //$NON-NLS-1$
private static final String COMPONENT_ANNOTATION = DSAnnotationCompilationParticipant.COMPONENT_ANNOTATION;
private static final String ACTIVATE_ANNOTATION = "org.osgi.service.component.annotations.Activate"; //$NON-NLS-1$
private static final String MODIFIED_ANNOTATION = "org.osgi.service.component.annotations.Modified"; //$NON-NLS-1$
private static final String DEACTIVATE_ANNOTATION = "org.osgi.service.component.annotations.Deactivate"; //$NON-NLS-1$
private static final String REFERENCE_ANNOTATION = "org.osgi.service.component.annotations.Reference"; //$NON-NLS-1$
private static final Pattern PID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*"); //$NON-NLS-1$
private static final String ATTRIBUTE_COMPONENT_CONFIGURATION_PID = "configuration-pid"; //$NON-NLS-1$
private static final String ATTRIBUTE_COMPONENT_REFERENCE = "reference"; //$NON-NLS-1$
private static final String ATTRIBUTE_SERVICE_SCOPE = "scope"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_POLICY_OPTION = "policy-option"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_UPDATED = "updated"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_SCOPE = "scope"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_FIELD = "field"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_FIELD_OPTION = "field-option"; //$NON-NLS-1$
private static final String ATTRIBUTE_REFERENCE_FIELD_COLLECTION_TYPE = "field-collection-type"; //$NON-NLS-1$
private static final String VALUE_SERVICE_SCOPE_DEFAULT = DSEnums.getServiceScope("DEFAULT"); //$NON-NLS-1$
private static final String VALUE_REFERENCE_FIELD_OPTION_REPLACE = DSEnums.getFieldOption("REPLACE"); //$NON-NLS-1$
private static final String VALUE_REFERENCE_FIELD_OPTION_UPDATE = DSEnums.getFieldOption("UPDATE"); //$NON-NLS-1$
private static final Set<String> PROPERTY_TYPES = Collections.unmodifiableSet(
new HashSet<>(
Arrays.asList(
null,
IDSConstants.VALUE_PROPERTY_TYPE_STRING,
IDSConstants.VALUE_PROPERTY_TYPE_LONG,
IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE,
IDSConstants.VALUE_PROPERTY_TYPE_FLOAT,
IDSConstants.VALUE_PROPERTY_TYPE_INTEGER,
IDSConstants.VALUE_PROPERTY_TYPE_BYTE,
IDSConstants.VALUE_PROPERTY_TYPE_CHAR,
IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN,
IDSConstants.VALUE_PROPERTY_TYPE_SHORT)));
private static final Map<String, String> PRIMITIVE_TYPE_MAP;
static {
HashMap<String, String> map = new HashMap<>(16);
map.put(Long.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_LONG);
map.put(Double.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE);
map.put(Float.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_FLOAT);
map.put(Integer.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_INTEGER);
map.put(Byte.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BYTE);
map.put(Character.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_CHAR);
map.put(Boolean.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
map.put(Short.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_SHORT);
map.put(Long.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_LONG);
map.put(Double.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE);
map.put(Float.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_FLOAT);
map.put(Integer.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_INTEGER);
map.put(Byte.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BYTE);
map.put(Character.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_CHAR);
map.put(Boolean.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
map.put(Short.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_SHORT);
PRIMITIVE_TYPE_MAP = Collections.unmodifiableMap(map);
}
private static final Comparator<IDSReference> REF_NAME_COMPARATOR = new Comparator<IDSReference>() {
@Override
public int compare(IDSReference o1, IDSReference o2) {
return o1.getReferenceName().compareTo(o2.getReferenceName());
}
};
private static final Debug debug = AnnotationProcessor.debug;
private final AnnotationProcessor processor;
private final ProjectState state;
private final DSAnnotationVersion specVersion;
private final ValidationErrorLevel errorLevel;
private final ValidationErrorLevel missingUnbindMethodLevel;
private final Map<String, String> dsKeys;
private final Set<DSAnnotationProblem> problems;
public AnnotationVisitor(AnnotationProcessor processor, ProjectState state, Map<String, String> dsKeys, Set<DSAnnotationProblem> problems) {
this.processor = processor;
this.state = state;
this.specVersion = state.getSpecVersion();
this.errorLevel = state.getErrorLevel();
this.missingUnbindMethodLevel = state.getMissingUnbindMethodLevel();
this.dsKeys = dsKeys;
this.problems = problems;
}
@Override
public boolean visit(TypeDeclaration type) {
if (!Modifier.isPublic(type.getModifiers())) {
// non-public types cannot be (or have nested) components
if (errorLevel.isIgnore()) {
return false;
}
Annotation annotation = findComponentAnnotation(type);
if (annotation != null) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notPublic, type.getName().getIdentifier()), type.getName().getIdentifier());
}
return true;
}
Annotation annotation = findComponentAnnotation(type);
if (annotation != null) {
boolean isInterface = false;
boolean isAbstract = false;
boolean isNested = false;
boolean noDefaultConstructor = false;
if ((isInterface = type.isInterface())
|| (isAbstract = Modifier.isAbstract(type.getModifiers()))
|| (isNested = (!type.isPackageMemberTypeDeclaration() && !isNestedPublicStatic(type)))
|| (noDefaultConstructor = !hasDefaultConstructor(type))) {
// interfaces, abstract types, non-static/non-public nested types, or types with no default constructor cannot be components
if (errorLevel != ValidationErrorLevel.ignore) {
if (isInterface) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_interface, type.getName().getIdentifier()), type.getName().getIdentifier());
} else if (isAbstract) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_abstract, type.getName().getIdentifier()), type.getName().getIdentifier());
} else if (isNested) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notTopLevel, type.getName().getIdentifier()), type.getName().getIdentifier());
} else if (noDefaultConstructor) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor, type.getName().getIdentifier()), type.getName().getIdentifier());
} else {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentImplementationClass, type.getName().getIdentifier()), type.getName().getIdentifier());
}
}
} else {
ITypeBinding typeBinding = type.resolveBinding();
if (typeBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for type: %s", type)); //$NON-NLS-1$
}
} else {
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
if (annotationBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$
}
} else {
try {
processComponent(type, typeBinding, annotation, annotationBinding, problems);
} catch (CoreException e) {
Activator.log(e);
}
}
}
}
}
return true;
}
@Override
public boolean visit(EnumDeclaration node) {
Annotation annotation = findComponentAnnotation(node);
if (annotation != null) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_enumeration, node.getName().getIdentifier()), node.getName().getIdentifier());
}
return false;
}
@Override
public boolean visit(AnnotationTypeDeclaration node) {
Annotation annotation = findComponentAnnotation(node);
if (annotation != null) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_annotation, node.getName().getIdentifier()), node.getName().getIdentifier());
}
return true;
}
private Annotation findComponentAnnotation(AbstractTypeDeclaration type) {
for (Object item : type.modifiers()) {
if (!(item instanceof Annotation)) {
continue;
}
Annotation annotation = (Annotation) item;
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
if (annotationBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$
}
continue;
}
if (COMPONENT_ANNOTATION.equals(annotationBinding.getAnnotationType().getQualifiedName())) {
return annotation;
}
}
return null;
}
private boolean isNestedPublicStatic(AbstractTypeDeclaration type) {
if (Modifier.isStatic(type.getModifiers())) {
ASTNode parent = type.getParent();
if (parent != null && (parent.getNodeType() == ASTNode.TYPE_DECLARATION || parent.getNodeType() == ASTNode.ANNOTATION_TYPE_DECLARATION)) {
AbstractTypeDeclaration parentType = (AbstractTypeDeclaration) parent;
if (Modifier.isPublic(parentType.getModifiers())) {
return parentType.isPackageMemberTypeDeclaration() || isNestedPublicStatic(parentType);
}
}
}
return false;
}
private boolean hasDefaultConstructor(TypeDeclaration type) {
boolean hasConstructor = false;
for (MethodDeclaration method : type.getMethods()) {
if (method.isConstructor()) {
hasConstructor = true;
if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) {
return true;
}
}
}
return !hasConstructor;
}
private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Collection<DSAnnotationProblem> problems) throws CoreException {
// determine component name
HashMap<String, Object> params = new HashMap<>();
for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) {
params.put(pair.getName(), pair.getValue());
}
String implClass = typeBinding.getBinaryName();
String name = implClass;
Object value;
if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$
name = (String) value;
validateComponentName(annotation, name, problems);
}
// set up document to edit
IPath path = new Path(state.getPath()).append(name).addFileExtension("xml"); //$NON-NLS-1$
String dsKey = path.toPortableString();
dsKeys.put(implClass, dsKey);
IProject project = typeBinding.getJavaElement().getJavaProject().getProject();
IFile file = PDEProject.getBundleRelativeFile(project, path);
IPath filePath = file.getFullPath();
processor.verifyOutputLocation(file);
// handle file move/rename
String oldPath = state.getModelFile(implClass);
if (oldPath != null && !oldPath.equals(dsKey) && !file.exists()) {
IFile oldFile = PDEProject.getBundleRelativeFile(project, Path.fromPortableString(oldPath));
if (oldFile.exists()) {
try {
oldFile.move(file.getFullPath(), true, true, null);
} catch (CoreException e) {
Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, String.format("Unable to move model file from '%s' to '%s'.", oldPath, file.getFullPath()), e)); //$NON-NLS-1$
}
}
}
ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
bufferManager.connect(filePath, LocationKind.IFILE, null);
ITextFileBuffer buffer = bufferManager.getTextFileBuffer(filePath, LocationKind.IFILE);
if (buffer.isDirty()) {
buffer.commit(null, true);
}
IDocument document = buffer.getDocument();
final DSModel dsModel = new DSModel(document, true);
dsModel.setUnderlyingResource(file);
dsModel.setCharset("UTF-8"); //$NON-NLS-1$
dsModel.load();
// note: we can't use XMLTextChangeListener because it generates overlapping edits!
// thus we replace the entire content with one edit (if changed)
final IDocument fDoc = document;
dsModel.addModelChangedListener(new IModelTextChangeListener() {
private final IDocument document = fDoc;
private boolean changed;
@Override
public void modelChanged(IModelChangedEvent event) {
changed = true;
}
@Override
public TextEdit[] getTextOperations() {
if (!changed) {
return new TextEdit[0];
}
String text = dsModel.getContents();
ReplaceEdit edit = new ReplaceEdit(0, document.getLength(), text);
return new TextEdit[] { edit };
}
@Override
public String getReadableName(TextEdit edit) {
return null;
}
});
try {
processComponent(dsModel, type, typeBinding, annotation, annotationBinding, params, name, implClass, problems);
TextEdit[] edits = dsModel.getLastTextChangeListener().getTextOperations();
if (edits.length > 0) {
if (debug.isDebugging()) {
debug.trace(String.format("Saving model: %s", file.getFullPath())); //$NON-NLS-1$
}
final MultiTextEdit edit = new MultiTextEdit();
edit.addChildren(edits);
if (buffer.isSynchronizationContextRequested()) {
final IDocument doc = document;
final CoreException[] ex = new CoreException[1];
final CountDownLatch latch = new CountDownLatch(1);
bufferManager.execute(new Runnable() {
@Override
public void run() {
try {
performEdit(doc, edit);
} catch (CoreException e) {
ex[0] = e;
}
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
if (debug.isDebugging())
debug.trace("Interrupted while waiting for edits to complete on display thread.", e); //$NON-NLS-1$
}
if (ex[0] != null) {
throw ex[0];
}
} else {
performEdit(document, edit);
}
buffer.commit(null, true);
}
} finally {
dsModel.dispose();
bufferManager.disconnect(buffer.getLocation(), LocationKind.IFILE, null);
}
}
private void performEdit(IDocument document, TextEdit edit) throws CoreException {
DocumentRewriteSession session = null;
try {
if (document instanceof IDocumentExtension4) {
session = ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
}
LinkedModeModel.closeAllModels(document);
edit.apply(document);
} catch (MalformedTreeException e) {
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$
} catch (BadLocationException e) {
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$
} finally {
if (session != null) {
((IDocumentExtension4) document).stopRewriteSession(session);
}
}
}
private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, ?> params, String name, String implClass, Collection<DSAnnotationProblem> problems) {
Object value;
Collection<String> services;
if ((value = params.get("service")) instanceof Object[]) { //$NON-NLS-1$
Object[] elements = (Object[]) value;
services = new LinkedHashSet<>(elements.length);
Map<String, Integer> serviceDuplicates = errorLevel.isIgnore() ? null : new HashMap<>();
for (int i = 0; i < elements.length; ++i) {
ITypeBinding serviceType = (ITypeBinding) elements[i];
String serviceName = serviceType.getBinaryName();
if (!errorLevel.isIgnore()) {
if (serviceDuplicates.containsKey(serviceName)) {
reportProblem(annotation, "service", i, problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$
Integer pos = serviceDuplicates.put(serviceName, null);
if (pos != null) {
reportProblem(annotation, "service", pos.intValue(), problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$
}
} else {
serviceDuplicates.put(serviceName, i);
}
}
services.add(serviceName);
validateComponentService(annotation, typeBinding, serviceType, i, problems);
}
} else {
ITypeBinding[] serviceTypes = typeBinding.getInterfaces();
services = new ArrayList<>(serviceTypes.length);
for (ITypeBinding serviceType : serviceTypes) {
services.add(serviceType.getBinaryName());
}
}
String factory = null;
if ((value = params.get("factory")) instanceof String) { //$NON-NLS-1$
factory = (String) value;
validateComponentFactory(annotation, factory, problems);
}
Boolean serviceFactory = null;
if ((value = params.get("servicefactory")) instanceof Boolean) { //$NON-NLS-1$
serviceFactory = (Boolean) value;
}
Boolean enabled = null;
if ((value = params.get("enabled")) instanceof Boolean) { //$NON-NLS-1$
enabled = (Boolean) value;
}
Boolean immediate = null;
if ((value = params.get("immediate")) instanceof Boolean) { //$NON-NLS-1$
immediate = (Boolean) value;
}
String[] properties;
if ((value = params.get("property")) instanceof Object[]) { //$NON-NLS-1$
Object[] elements = (Object[]) value;
ArrayList<String> list = new ArrayList<>(elements.length);
for (Object element : elements) {
if (element instanceof String) {
list.add((String) element);
}
}
properties = list.toArray(new String[list.size()]);
} else {
properties = new String[0];
}
String[] propertyFiles;
if ((value = params.get("properties")) instanceof Object[]) { //$NON-NLS-1$
Object[] elements = (Object[]) value;
ArrayList<String> list = new ArrayList<>(elements.length);
for (Object element : elements) {
if (element instanceof String) {
list.add((String) element);
}
}
propertyFiles = list.toArray(new String[list.size()]);
validateComponentPropertyFiles(annotation, ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles, problems);
} else {
propertyFiles = new String[0];
}
String configPolicy = null;
if ((value = params.get("configurationPolicy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding configPolicyBinding = (IVariableBinding) value;
configPolicy = DSEnums.getConfigurationPolicy(configPolicyBinding.getName());
}
String configPid = null;
if ((value = params.get("configurationPid")) instanceof String) { //$NON-NLS-1$
configPid = (String) value;
validateComponentConfigPID(annotation, configPid, -1, problems);
} else if (specVersion == DSAnnotationVersion.V1_3 && value instanceof Object[]) {
// TODO validate duplicate PIDs!
LinkedHashSet<String> configPids = new LinkedHashSet<>(1);
int i = 0;
for (Object configPidElem : ((Object[]) value)) {
String configPidStr = String.valueOf(configPidElem);
if ("$".equals(configPidStr)) { //$NON-NLS-1$
configPids.add(name);
} else {
configPids.add(configPidStr);
validateComponentConfigPID(annotation, configPidStr, i, problems);
}
i++;
}
StringBuilder configPidBuf = new StringBuilder();
for (String configPidElem : configPids) {
if (configPidBuf.length() > 0) {
configPidBuf.append(' ');
}
configPidBuf.append(configPidElem);
}
configPid = configPidBuf.toString();
}
String serviceScope = null;
if (specVersion == DSAnnotationVersion.V1_3 && (value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding serviceScopeBinding = (IVariableBinding) value;
serviceScope = DSEnums.getServiceScope(serviceScopeBinding.getName());
}
IDSComponent component = model.getDSComponent();
if (enabled == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ENABLED, IDSConstants.VALUE_TRUE);
} else {
component.setEnabled(enabled.booleanValue());
}
if (name == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_NAME, null);
} else {
component.setAttributeName(name);
}
if (factory == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_FACTORY, null);
} else {
component.setFactory(factory);
}
if (immediate == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_IMMEDIATE, null);
} else {
component.setImmediate(immediate.booleanValue());
}
if (configPolicy == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_CONFIGURATION_POLICY, IDSConstants.VALUE_CONFIGURATION_POLICY_OPTIONAL);
} else {
component.setConfigurationPolicy(configPolicy);
}
DSAnnotationVersion requiredVersion = DSAnnotationVersion.V1_1;
if (configPid == null) {
removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null);
} else {
component.setXMLAttribute(ATTRIBUTE_COMPONENT_CONFIGURATION_PID, configPid);
requiredVersion = DSAnnotationVersion.V1_2;
}
IDSDocumentFactory dsFactory = model.getFactory();
IDSService service = component.getService();
if (services.isEmpty()) {
if (service != null) {
component.removeService(service);
}
} else {
if (service == null) {
service = dsFactory.createService();
// insert service element after last property or properties element
int firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
component.addChildNode(service, firstPos, true);
}
if (serviceScope == null || serviceScope.equals(VALUE_SERVICE_SCOPE_DEFAULT)) {
removeAttribute(service, "scope", null); //$NON-NLS-1$
} else {
service.setXMLAttribute(ATTRIBUTE_SERVICE_SCOPE, serviceScope);
requiredVersion = DSAnnotationVersion.V1_3;
}
IDSProvide[] provides = service.getProvidedServices();
HashMap<String, IDSProvide> provideMap = new HashMap<>(provides.length);
for (IDSProvide provide : provides) {
provideMap.put(provide.getInterface(), provide);
}
ArrayList<IDSProvide> provideList = new ArrayList<>(services.size());
for (String serviceName : services) {
IDSProvide provide = provideMap.remove(serviceName);
if (provide == null) {
provide = dsFactory.createProvide();
provide.setInterface(serviceName);
}
provideList.add(provide);
}
int firstPos = provides.length == 0 ? -1 : service.indexOf(provides[0]);
removeChildren(service, (provideMap.values()));
addOrMoveChildren(service, provideList, firstPos);
if (serviceFactory == null) {
removeAttribute(service, IDSConstants.ATTRIBUTE_SERVICE_FACTORY, IDSConstants.VALUE_FALSE);
} else {
service.setServiceFactory(serviceFactory.booleanValue());
}
}
ArrayList<IDSReference> references = new ArrayList<>();
HashMap<String, Annotation> referenceNames = new HashMap<>();
IDSReference[] refElements = component.getReferences();
HashMap<String, IDSReference> refMap = new HashMap<>(refElements.length);
for (IDSReference refElement : refElements) {
String referenceName = refElement.getReferenceName();
if (referenceName == null) {
referenceName = refElement.getXMLAttributeValue(ATTRIBUTE_REFERENCE_FIELD);
if (referenceName == null) {
referenceName = refElement.getReferenceBind();
if (referenceName == null) {
referenceName = refElement.getReferenceInterface();
}
}
}
refMap.put(referenceName, refElement);
}
if (annotation.isNormalAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
for (Object annotationValue : ((NormalAnnotation) annotation).values()) {
MemberValuePair annotationMemberValuePair = (MemberValuePair) annotationValue;
if (!ATTRIBUTE_COMPONENT_REFERENCE.equals(annotationMemberValuePair.getName().getIdentifier())) {
continue;
}
ArrayList<Annotation> annotations = new ArrayList<>();
Expression memberValue = annotationMemberValuePair.getValue();
if (memberValue instanceof Annotation) {
annotations.add((Annotation) memberValue);
} else if (memberValue instanceof ArrayInitializer) {
for (Object memberValueElement : ((ArrayInitializer) memberValue).expressions()) {
if (memberValueElement instanceof Annotation) {
annotations.add((Annotation) memberValueElement);
}
}
}
for (Annotation referenceAnnotation : annotations) {
IAnnotationBinding referenceAnnotationBinding = referenceAnnotation.resolveAnnotationBinding();
if (referenceAnnotationBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for annotation: %s", referenceAnnotation)); //$NON-NLS-1$
}
continue;
}
String annotationName = referenceAnnotationBinding.getAnnotationType().getQualifiedName();
if (!REFERENCE_ANNOTATION.equals(annotationName)) {
continue;
}
HashMap<String, Object> annotationParams = new HashMap<>();
for (IMemberValuePairBinding pair : referenceAnnotationBinding.getDeclaredMemberValuePairs()) {
annotationParams.put(pair.getName(), pair.getValue());
}
String referenceName = (String) annotationParams.get(IDSConstants.ATTRIBUTE_REFERENCE_NAME);
IDSReference reference = refMap.remove(referenceName);
if (reference == null) {
reference = createReference(dsFactory);
}
references.add(reference);
processReference(reference, referenceAnnotation, referenceAnnotationBinding, annotationParams, referenceNames, problems);
requiredVersion = DSAnnotationVersion.V1_3;
}
}
}
if (specVersion == DSAnnotationVersion.V1_3) {
for (FieldDeclaration field : type.getFields()) {
for (Object modifier : field.modifiers()) {
if (!(modifier instanceof Annotation)) {
continue;
}
Annotation fieldAnnotation = (Annotation) modifier;
IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding();
if (fieldAnnotationBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$
}
continue;
}
String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName();
if (!REFERENCE_ANNOTATION.equals(annotationName)) {
continue;
}
HashMap<String, Object> annotationParams = null;
// TODO do we really care about all fragments??
for (Object fragmentElement : field.fragments()) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentElement;
IVariableBinding fieldBinding = fragment.resolveBinding();
if (fieldBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for field: %s", fragment)); //$NON-NLS-1$
}
continue;
}
if (annotationParams == null) {
annotationParams = new HashMap<>();
for (IMemberValuePairBinding pair : fieldAnnotationBinding.getDeclaredMemberValuePairs()) {
annotationParams.put(pair.getName(), pair.getValue());
}
}
String referenceName = (String) annotationParams.get("name"); //$NON-NLS-1$
if (referenceName == null) {
referenceName = fieldBinding.getName();
}
IDSReference reference = refMap.remove(referenceName);
if (reference == null) {
reference = createReference(dsFactory);
}
references.add(reference);
processReference(reference, field, fieldBinding, fieldAnnotation, fieldAnnotationBinding, annotationParams, referenceNames, problems);
requiredVersion = DSAnnotationVersion.V1_3;
}
}
}
}
String activate = null;
boolean lookedForActivateMethod = false;
IMethodBinding activateMethod = null;
Annotation activateAnnotation = null;
String deactivate = null;
boolean lookedForDeactivateMethod = false;
IMethodBinding deactivateMethod = null;
Annotation deactivateAnnotation = null;
String modified = null;
IMethodBinding modifiedMethod = null;
Annotation modifiedAnnotation = null;
for (MethodDeclaration method : type.getMethods()) {
for (Object modifier : method.modifiers()) {
if (!(modifier instanceof Annotation)) {
continue;
}
Annotation methodAnnotation = (Annotation) modifier;
IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding();
if (methodAnnotationBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$
}
continue;
}
String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName();
if (ACTIVATE_ANNOTATION.equals(annotationName)) {
if (activate == null) {
activate = method.getName().getIdentifier();
if (specVersion == DSAnnotationVersion.V1_3) {
activateMethod = method.resolveBinding();
}
activateAnnotation = methodAnnotation;
validateLifeCycleMethod(methodAnnotation, "activate", method, problems); //$NON-NLS-1$
} else if (!errorLevel.isIgnore()) {
reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier());
if (activateAnnotation != null) {
reportProblem(activateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, activate);
activateAnnotation = null;
}
}
continue;
}
if (DEACTIVATE_ANNOTATION.equals(annotationName)) {
if (deactivate == null) {
deactivate = method.getName().getIdentifier();
if (specVersion == DSAnnotationVersion.V1_3) {
deactivateMethod = method.resolveBinding();
}
deactivateAnnotation = methodAnnotation;
validateLifeCycleMethod(methodAnnotation, "deactivate", method, problems); //$NON-NLS-1$
} else if (!errorLevel.isIgnore()) {
reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier());
if (deactivateAnnotation != null) {
reportProblem(deactivateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate);
deactivateAnnotation = null;
}
}
continue;
}
if (MODIFIED_ANNOTATION.equals(annotationName)) {
if (modified == null) {
modified = method.getName().getIdentifier();
if (specVersion == DSAnnotationVersion.V1_3) {
modifiedMethod = method.resolveBinding();
}
modifiedAnnotation = methodAnnotation;
validateLifeCycleMethod(methodAnnotation, "modified", method, problems); //$NON-NLS-1$
} else if (!errorLevel.isIgnore()) {
reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier());
if (modifiedAnnotation != null) {
reportProblem(modifiedAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, modified);
modifiedAnnotation = null;
}
}
continue;
}
if (REFERENCE_ANNOTATION.equals(annotationName)) {
IMethodBinding methodBinding = method.resolveBinding();
if (methodBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$
}
} else {
HashMap<String, Object> annotationParams = new HashMap<>();
for (IMemberValuePairBinding pair : methodAnnotationBinding.getDeclaredMemberValuePairs()) {
annotationParams.put(pair.getName(), pair.getValue());
}
String referenceName = getReferenceName(methodBinding.getName(), annotationParams);
IDSReference reference = refMap.remove(referenceName);
if (reference == null) {
reference = createReference(dsFactory);
}
references.add(reference);
requiredVersion = requiredVersion.max(processReference(reference, method, methodBinding, methodAnnotation, methodAnnotationBinding, annotationParams, referenceNames, problems));
}
continue;
}
}
}
if (activate == null) {
// only remove activate="activate" if method not found
if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$
|| ((lookedForActivateMethod = true)
&& (activateMethod = findLifeCycleMethod(typeBinding, "activate")) == null)) { //$NON-NLS-1$
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null);
}
} else {
component.setActivateMethod(activate);
}
if (deactivate == null) {
// only remove deactivate="deactivate" if method not found
if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$
|| ((lookedForDeactivateMethod = true)
&& (deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate")) == null)) { //$NON-NLS-1$
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null);
}
} else {
component.setDeactivateMethod(deactivate);
}
if (modified == null) {
removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null);
} else {
component.setModifiedeMethod(modified);
}
ArrayList<IDSProperty> propList = new ArrayList<>();
if (specVersion == DSAnnotationVersion.V1_3) {
// collect component property types from activate, modified, and deactivate methods
if (activateMethod == null && !lookedForActivateMethod) {
activateMethod = findLifeCycleMethod(typeBinding, "activate"); //$NON-NLS-1$
}
if (deactivateMethod == null && !lookedForDeactivateMethod) {
deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate"); //$NON-NLS-1$
}
HashSet<ITypeBinding> cptClosure = new HashSet<>();
if (activateMethod != null) {
collectProperties(activateMethod, dsFactory, propList, cptClosure);
}
if (modifiedMethod != null) {
collectProperties(modifiedMethod, dsFactory, propList, cptClosure);
}
if (deactivateMethod != null) {
collectProperties(deactivateMethod, dsFactory, propList, cptClosure);
}
}
IDSProperty[] propElements = component.getPropertyElements();
if (propList.isEmpty() && properties.length == 0) {
removeChildren(component, Arrays.asList(propElements));
} else {
// build up new property elements
LinkedHashMap<String, IDSProperty> map = new LinkedHashMap<>(properties.length);
for (int i = 0; i < properties.length; ++i) {
String propertyStr = properties[i];
String[] pair = propertyStr.split("=", 2); //$NON-NLS-1$
int colon = pair[0].indexOf(':');
String propertyName, propertyType;
if (colon == -1) {
propertyName = pair[0];
propertyType = null;
} else {
propertyName = pair[0].substring(0, colon);
propertyType = pair[0].substring(colon + 1);
}
String propertyValue = pair.length > 1 ? pair[1].trim() : null;
IDSProperty property = map.get(propertyName);
if (property == null) {
// create a new property
property = dsFactory.createProperty();
map.put(propertyName, property);
property.setPropertyName(propertyName);
if (propertyType == null) {
removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_TYPE, null); // just remove the attribute completely so we can detect changes when reconciling
} else {
property.setPropertyType(propertyType);
}
property.setPropertyValue(propertyValue);
validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems);
} else {
// property is multi-valued
String content = property.getPropertyElemBody();
if (content == null) {
content = property.getPropertyValue();
property.setPropertyElemBody(content);
property.setPropertyValue(null);
}
if (!errorLevel.isIgnore()) {
String expected = property.getPropertyType() == null || property.getPropertyType().length() == 0 || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType()) ? Messages.AnnotationProcessor_stringOrEmpty : property.getPropertyType();
String actual = propertyType == null || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(propertyType) ? Messages.AnnotationProcessor_stringOrEmpty : propertyType;
if (!actual.equals(expected)) {
reportProblem(annotation, "property", i, problems, NLS.bind(Messages.AnnotationProcessor_inconsistentComponentPropertyType, actual, expected), actual); //$NON-NLS-1$
} else {
validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems);
}
}
if (propertyValue != null) {
property.setPropertyElemBody(content + "\n" + pair[1]); //$NON-NLS-1$
}
}
}
// reconcile against existing property elements
HashMap<String, List<IDSProperty>> propMap = new HashMap<>(propElements.length);
for (IDSProperty propElement : propElements) {
List<IDSProperty> duplicates = propMap.get(propElement.getPropertyName());
if (duplicates == null) {
duplicates = new LinkedList<>();
propMap.put(propElement.getPropertyName(), duplicates);
}
duplicates.add(propElement);
}
propList.addAll(map.values());
for (ListIterator<IDSProperty> i = propList.listIterator(); i.hasNext();) {
IDSProperty newProperty = i.next();
List<IDSProperty> propertyElemList = propMap.get(newProperty.getPropertyName());
if (propertyElemList == null) {
continue;
}
IDSProperty property = propertyElemList.remove(0);
if (propertyElemList.isEmpty()) {
propMap.remove(newProperty.getPropertyName());
}
i.set(property);
String newPropertyType = newProperty.getPropertyType();
if (newPropertyType != null || !IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType())) {
property.setPropertyType(newPropertyType);
}
String newContent = newProperty.getPropertyElemBody();
if (newContent == null) {
property.setPropertyValue(newProperty.getPropertyValue());
IDocumentTextNode textNode = property.getTextNode();
if (textNode != null) {
property.removeTextNode();
if (property.isInTheModel() && property.isEditable()) {
model.fireModelChanged(new ModelChangedEvent(model, IModelChangedEvent.REMOVE, new Object[] { textNode }, null));
}
}
} else {
removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
String content = property.getPropertyElemBody();
if (content == null || !newContent.equals(normalizePropertyElemBody(content))) {
property.setPropertyElemBody(newContent);
}
}
}
int firstPos = propElements.length == 0
? 0 // insert first property element as first child of component
: component.indexOf(propElements[0]);
ArrayList<IDSProperty> leftovers = new ArrayList<>();
for (List<IDSProperty> propertyElementList : propMap.values()) {
leftovers.addAll(propertyElementList);
}
removeChildren(component, leftovers);
addOrMoveChildren(component, propList, firstPos);
}
IDSProperties[] propFileElements = component.getPropertiesElements();
if (propertyFiles.length == 0) {
removeChildren(component, Arrays.asList(propFileElements));
} else {
HashMap<String, IDSProperties> propFileMap = new HashMap<>(propFileElements.length);
for (IDSProperties propFileElement : propFileElements) {
propFileMap.put(propFileElement.getEntry(), propFileElement);
}
ArrayList<IDSProperties> propFileList = new ArrayList<>(propertyFiles.length);
for (String propertyFile : propertyFiles) {
IDSProperties propertiesElement = propFileMap.remove(propertyFile);
if (propertiesElement == null) {
propertiesElement = dsFactory.createProperties();
propertiesElement.setInTheModel(false); // note: workaround for PDE bug
propertiesElement.setEntry(propertyFile);
}
propFileList.add(propertiesElement);
}
int firstPos;
if (propFileElements.length == 0) {
// insert first properties element after last property or (if none) first child of component
propElements = component.getPropertyElements();
firstPos = propElements.length == 0 ? 0 : component.indexOf(propElements[propElements.length - 1]) + 1;
} else {
firstPos = component.indexOf(propFileElements[0]);
}
removeChildren(component, propFileMap.values());
addOrMoveChildren(component, propFileList, firstPos);
}
if (references.isEmpty()) {
removeChildren(component, Arrays.asList(refElements));
} else {
// references must be declared in ascending lexicographical order of their names
Collections.sort(references, REF_NAME_COMPARATOR);
int firstPos;
if (refElements.length == 0) {
// insert first reference element after service element, or (if not present) last property or properties
service = component.getService();
if (service == null) {
firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
} else {
firstPos = component.indexOf(service) + 1;
}
} else {
firstPos = component.indexOf(refElements[0]);
}
removeChildren(component, refMap.values());
addOrMoveChildren(component, references, firstPos);
}
IDSImplementation impl = component.getImplementation();
if (impl == null) {
impl = dsFactory.createImplementation();
component.setImplementation(impl);
}
impl.setClassName(implClass);
String xmlns = requiredVersion.getNamespace();
if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$
xmlns = (String) value;
validateComponentXMLNS(annotation, xmlns, requiredVersion, problems);
}
component.setNamespace(xmlns);
}
private IDSReference createReference(IDSDocumentFactory dsFactory) {
IDSReference reference = dsFactory.createReference();
// reset unnecessary defaults set by PDE
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, null);
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, null);
return reference;
}
private void removeChildren(IDSObject parent, Collection<? extends IDocumentElementNode> children) {
for (IDocumentElementNode child : children) {
parent.removeChildNode(child, true);
}
}
private void removeAttribute(IDSObject obj, String name, String defaultValue) {
IDocumentAttributeNode attrNode = obj.getDocumentAttribute(name);
if (attrNode != null) {
// only remove if value is not default
String value = attrNode.getAttributeValue();
if (value != null && value.equals(defaultValue)) {
return;
}
obj.removeDocumentAttribute(attrNode);
if (obj.isInTheModel() && obj.isEditable()) {
obj.getModel().fireModelChanged(new ModelChangedEvent(obj.getModel(), ModelChangedEvent.REMOVE, new Object[] { attrNode }, null));
}
}
}
private void addOrMoveChildren(IDSObject parent, List<? extends IDSObject> children, int firstPos) {
for (int i = 0, n = children.size(); i < n; ++i) {
IDSObject child = children.get(i);
if (child.isInTheModel()) {
int pos = parent.indexOf(child);
if (i == 0) {
if (firstPos < pos) {
// move to first place
moveChildNode(parent, child, firstPos - pos, true);
}
} else {
int prevPos = parent.indexOf(children.get(i - 1));
if (prevPos > pos) {
// move to previous sibling's position
moveChildNode(parent, child, prevPos - pos, true);
}
}
} else {
if (i == 0) {
if (firstPos == -1) {
parent.addChildNode(child, true);
} else {
// insert into first place
parent.addChildNode(child, firstPos, true);
}
} else {
// insert after preceding sibling
parent.addChildNode(child, parent.indexOf(children.get(i - 1)) + 1, true);
}
}
}
}
private void moveChildNode(IDocumentObject obj, IDocumentElementNode node, int newRelativeIndex, boolean fireEvent) {
if (newRelativeIndex == 1 || newRelativeIndex == -1) {
obj.moveChildNode(node, newRelativeIndex, fireEvent);
return;
}
// workaround for PDE's busted DocumentObject.clone() method
int currentIndex = obj.indexOf(node);
if (currentIndex == -1) {
return;
}
int newIndex = newRelativeIndex + currentIndex;
if (newIndex < 0 || newIndex >= obj.getChildCount()) {
return;
}
obj.removeChildNode(node, fireEvent);
IDocumentElementNode clone = clone(obj, node);
obj.addChildNode(clone, newIndex, fireEvent);
}
private IDocumentElementNode clone(IDocumentObject obj, IDocumentElementNode node) {
// note: same exact impl as DocumentObject.clone()
// but here the deserialized object will actually resolve successfully
// because our classloader (with DSPropery visible) will be on top of the stack
// yay for Java serialization, *sigh*
IDocumentElementNode clone = null;
try {
// Serialize
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(node);
out.flush();
out.close();
byte[] bytes = bout.toByteArray();
// Deserialize
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(bin);
clone = (IDocumentElementNode) in.readObject();
in.close();
// Reconnect
clone.reconnect(obj, obj.getSharedModel());
} catch (IOException e) {
if (debug.isDebugging())
debug.trace("Error cloning element.", e); //$NON-NLS-1$
} catch (ClassNotFoundException e) {
if (debug.isDebugging())
debug.trace("Error cloning element.", e); //$NON-NLS-1$
}
return clone;
}
private int indexOfLastPropertyOrProperties(IDSComponent component) {
int pos = -1;
IDSProperty[] propElements = component.getPropertyElements();
IDSProperties[] propFileElements = component.getPropertiesElements();
if (propElements.length > 0) {
pos = component.indexOf(propElements[propElements.length - 1]) + 1;
}
if (propFileElements.length > 0) {
int lastPos = component.indexOf(propFileElements[propFileElements.length - 1]) + 1;
if (lastPos > pos) {
pos = lastPos;
}
}
return pos;
}
private String normalizePropertyElemBody(String content) {
StringBuilder buf = new StringBuilder(content.length());
BufferedReader reader = new BufferedReader(new StringReader(content));
try {
String line;
while ((line = reader.readLine()) != null) {
String trimmed = line.trim();
if (trimmed.length() == 0) {
continue;
}
if (buf.length() > 0) {
buf.append('\n');
}
buf.append(trimmed);
}
} catch (IOException e) {
if (debug.isDebugging())
debug.trace("Error reading property element body.", e); //$NON-NLS-1$
} finally {
try {
reader.close();
} catch (IOException e) {
// ignore
}
}
return buf.toString();
}
private void collectProperties(IMethodBinding method, IDSDocumentFactory factory, Collection<IDSProperty> properties, Collection<ITypeBinding> visited) {
for (ITypeBinding paramTypeBinding : method.getParameterTypes()) {
if (!paramTypeBinding.isAnnotation() || !visited.add(paramTypeBinding)) {
continue;
}
for (IMethodBinding methodBinding : paramTypeBinding.getDeclaredMethods()) {
if (!methodBinding.isAnnotationMember()) {
continue;
}
Object value = methodBinding.getDefaultValue();
if (value == null || (value instanceof Object[] && ((Object[]) value).length == 0)) {
continue;
}
ITypeBinding returnType = methodBinding.getReturnType();
if (returnType.isArray() ? returnType.getElementType().isAnnotation() : returnType.isAnnotation()) {
// TODO per spec we should report error, but we may have no annotation to report it on!
continue;
}
IDSProperty property = factory.createProperty();
property.setPropertyName(createPropertyName(methodBinding.getName()));
property.setPropertyType(getPropertyType(returnType));
if (returnType.isArray()) {
StringBuilder body = new StringBuilder();
for (Object item : ((Object[]) value)) {
String itemValue = getPropertyValue(item);
if (itemValue == null || (itemValue = itemValue.trim()).isEmpty()) {
continue;
}
if (body.length() > 0) {
body.append('\n');
}
body.append(itemValue);
}
property.setPropertyElemBody(body.toString());
} else {
property.setPropertyValue(getPropertyValue(value));
}
properties.add(property);
}
}
}
private String createPropertyName(String name) {
StringBuilder buf = new StringBuilder(name.length());
char[] chars = name.toCharArray();
for (int i = 0, n = chars.length; i < n; ++i) {
if (chars[i] == '$') {
if (i == n - 1 || chars[i + 1] != '$') {
continue;
}
i++;
} else if (chars[i] == '_') {
if (i == n - 1 || chars[i + 1] != '_') {
chars[i] = '.';
} else {
i++;
}
}
buf.append(chars[i]);
}
return buf.toString();
}
private String getPropertyType(ITypeBinding type) {
if (type.isArray()) {
return getPropertyType(type.getElementType());
}
if (type.isPrimitive()) {
String name = type.getQualifiedName();
String result = PRIMITIVE_TYPE_MAP.get(name);
if (result != null) {
return result;
}
}
return IDSConstants.VALUE_PROPERTY_TYPE_STRING;
}
private String getPropertyValue(Object value) {
// enum
if (value instanceof IVariableBinding) {
return ((IVariableBinding) value).getName();
}
// class
if (value instanceof ITypeBinding) {
return ((ITypeBinding) value).getQualifiedName();
}
// everything else
return String.valueOf(value);
}
private void validateComponentName(Annotation annotation, String name, Collection<DSAnnotationProblem> problems) {
if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(name).matches()) {
reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentName, name), name); //$NON-NLS-1$
}
}
private void validateComponentService(Annotation annotation, ITypeBinding componentType, ITypeBinding serviceType, int index, Collection<DSAnnotationProblem> problems) {
if (!errorLevel.isIgnore() && !componentType.isAssignmentCompatible(serviceType)) {
reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentService, serviceType.getName()), serviceType.getName()); //$NON-NLS-1$
}
}
private void validateComponentFactory(Annotation annotation, String factory, Collection<DSAnnotationProblem> problems) {
if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(factory).matches()) {
reportProblem(annotation, "factory", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentFactoryName, factory), factory); //$NON-NLS-1$
}
}
private void validateComponentProperty(Annotation annotation, String name, String type, String value, int index, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
if (PROPERTY_TYPES.contains(type)) {
if (name == null || name.trim().length() == 0) {
reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_nameRequired, name); //$NON-NLS-1$
}
if (value == null) {
reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_valueRequired, name); //$NON-NLS-1$
} else {
try {
if (IDSConstants.VALUE_PROPERTY_TYPE_LONG.equals(type)) {
Long.valueOf(value);
} else if (IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE.equals(type)) {
Double.valueOf(value);
} else if (IDSConstants.VALUE_PROPERTY_TYPE_FLOAT.equals(type)) {
Float.valueOf(value);
} else if (IDSConstants.VALUE_PROPERTY_TYPE_INTEGER.equals(type) || IDSConstants.VALUE_PROPERTY_TYPE_CHAR.equals(type)) {
Integer.valueOf(value);
} else if (IDSConstants.VALUE_PROPERTY_TYPE_BYTE.equals(type)) {
Byte.valueOf(value);
} else if (IDSConstants.VALUE_PROPERTY_TYPE_SHORT.equals(type)) {
Short.valueOf(value);
}
} catch (NumberFormatException e) {
reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyValue, type, value), String.valueOf(value)); //$NON-NLS-1$
}
}
} else {
reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyType, type), String.valueOf(type)); //$NON-NLS-1$
}
}
private void validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
for (int i = 0; i < files.length; ++i) {
String file = files[i];
IFile wsFile = PDEProject.getBundleRelativeFile(project, new Path(file));
if (!wsFile.exists()) {
reportProblem(annotation, "properties", i, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file), file); //$NON-NLS-1$
}
}
}
private void validateComponentXMLNS(Annotation annotation, String xmlns, DSAnnotationVersion requiredVersion, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
DSAnnotationVersion specifiedVersion = DSAnnotationVersion.fromNamespace(xmlns);
if (requiredVersion.compareTo(specifiedVersion) > 0) {
reportProblem(annotation, "xmlns", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentDescriptorNamespace, xmlns), xmlns); //$NON-NLS-1$
}
}
private void validateComponentConfigPID(Annotation annotation, String configPid, int index, Collection<DSAnnotationProblem> problems) {
if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(configPid).matches()) {
reportProblem(annotation, "configurationPid", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$
}
}
private void validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
IMethodBinding methodBinding = method.resolveBinding();
if (methodBinding == null) {
if (debug.isDebugging()) {
debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$
}
return;
}
if (Modifier.isStatic(methodBinding.getModifiers())) {
reportProblem(annotation, methodName, problems, Messages.AnnotationProcessor_invalidLifecycleMethod_static);
}
String returnTypeName = methodBinding.getReturnType().getName();
if (!Void.TYPE.getName().equals(returnTypeName)) {
reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodReturnType, methodName, returnTypeName), returnTypeName);
}
ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes();
if (paramTypeBindings.length == 0) {
// no-arg method
return;
}
// every argument must be either Map, Annotation (component property type), ComponentContext, or BundleContext
boolean hasMap = false;
boolean hasCompCtx = false;
boolean hasBundleCtx = false;
boolean hasInt = false;
HashSet<ITypeBinding> annotationParams = new HashSet<>(1);
for (ITypeBinding paramTypeBinding : paramTypeBindings) {
ITypeBinding paramTypeErasure = paramTypeBinding.getErasure();
String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
boolean isDuplicate = false;
if (paramTypeBinding.isAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
if (!annotationParams.add(paramTypeBinding)) {
isDuplicate = true;
}
} else if (Map.class.getName().equals(paramTypeName)) {
if (hasMap) {
isDuplicate = true;
} else {
hasMap = true;
}
} else if (COMPONENT_CONTEXT.equals(paramTypeName)) {
if (hasCompCtx) {
isDuplicate = true;
} else {
hasCompCtx = true;
}
} else if (BundleContext.class.getName().equals(paramTypeName)) {
if (hasBundleCtx) {
isDuplicate = true;
} else {
hasBundleCtx = true;
}
} else if ("deactivate".equals(methodName) //$NON-NLS-1$
&& (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) {
if (hasInt) {
isDuplicate = true;
} else {
hasInt = true;
}
} else {
reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName);
}
if (isDuplicate) {
reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_duplicateLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName);
}
}
}
private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String methodName) {
for (IMethodBinding methodBinding : componentClass.getDeclaredMethods()) {
if (methodName.equals(methodBinding.getName())
&& Void.TYPE.getName().equals(methodBinding.getReturnType().getName())) {
ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes();
// every argument must be either Map, Annotation (component property type), ComponentContext, or BundleContext
boolean hasMap = false;
boolean hasCompCtx = false;
boolean hasBundleCtx = false;
boolean hasInt = false;
boolean isInvalid = false;
HashSet<ITypeBinding> annotationParams = new HashSet<>(1);
for (ITypeBinding paramTypeBinding : paramTypeBindings) {
if (paramTypeBinding.isAnnotation()) {
if (specVersion == DSAnnotationVersion.V1_3 && annotationParams.add(paramTypeBinding)) {
// component property type (multiple arguments allowed)
continue;
}
isInvalid = true;
break;
}
ITypeBinding paramTypeErasure = paramTypeBinding.getErasure();
String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
if (Map.class.getName().equals(paramTypeName)) {
if (hasMap) {
isInvalid = true;
} else {
hasMap = true;
}
} else if (COMPONENT_CONTEXT.equals(paramTypeName)) {
if (hasCompCtx) {
isInvalid = true;
} else {
hasCompCtx = true;
}
} else if (BundleContext.class.getName().equals(paramTypeName)) {
if (hasBundleCtx) {
isInvalid = true;
} else {
hasBundleCtx = true;
}
} else if ("deactivate".equals(methodName) //$NON-NLS-1$
&& (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) {
if (hasInt) {
isInvalid = true;
} else {
hasInt = true;
}
} else {
isInvalid = true;
}
if (isInvalid) {
break;
}
}
if (!isInvalid) {
return methodBinding;
}
}
}
return null;
}
private DSAnnotationVersion processReference(IDSReference reference, MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, Object> params, Map<String, Annotation> names, Collection<DSAnnotationProblem> problems) {
ITypeBinding[] argTypes = methodBinding.getParameterTypes();
ITypeBinding serviceType;
Object value;
if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$
serviceType = (ITypeBinding) value;
if (!errorLevel.isIgnore() && argTypes.length > 0) {
String erasure = argTypes[0].getErasure().getBinaryName();
ITypeBinding[] typeArgs;
if (!((ServiceReference.class.getName().equals(erasure) || (specVersion == DSAnnotationVersion.V1_3 && COMPONENT_SERVICE_OBJECTS.equals(erasure)))
&& ((typeArgs = argTypes[0].getTypeArguments()).length == 0 || serviceType.isAssignmentCompatible(typeArgs[0])))
&& !serviceType.isAssignmentCompatible(argTypes[0])) {
reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, argTypes[0].getName(), serviceType.getName()), argTypes[0].getName(), serviceType.getName()); //$NON-NLS-1$
}
}
} else if (argTypes.length > 0) {
String erasure = argTypes[0].getErasure().getBinaryName();
if (ServiceReference.class.getName().equals(erasure) || (specVersion == DSAnnotationVersion.V1_3 && COMPONENT_SERVICE_OBJECTS.equals(erasure))) {
ITypeBinding[] typeArgs = argTypes[0].getTypeArguments();
if (typeArgs.length > 0) {
serviceType = typeArgs[0];
} else {
serviceType = null;
}
} else {
serviceType = argTypes[0].isPrimitive() ? getObjectType(method.getAST(), argTypes[0]) : argTypes[0];
}
} else {
serviceType = null;
}
if (serviceType == null) {
reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReferenceServiceUnknown);
serviceType = method.getAST().resolveWellKnownType(Object.class.getName());
}
validateReferenceBindMethod(annotation, serviceType, methodBinding, problems);
String service = serviceType == null ? null : serviceType.getBinaryName();
String methodName = methodBinding.getName();
String name = getReferenceName(methodName, params);
if (!errorLevel.isIgnore()) {
if (names.containsKey(name)) {
reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
Annotation duplicate = names.put(name, null);
if (duplicate != null) {
reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
}
} else {
names.put(name, annotation);
}
}
String cardinality = null;
if ((value = params.get("cardinality")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding cardinalityBinding = (IVariableBinding) value;
cardinality = DSEnums.getReferenceCardinality(cardinalityBinding.getName());
}
String policy = null;
if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyBinding = (IVariableBinding) value;
policy = DSEnums.getReferencePolicy(policyBinding.getName());
}
String target = null;
if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$
target = (String) value;
validateReferenceTarget(annotation, target, problems);
}
String unbind;
if ((value = params.get("unbind")) instanceof String) { //$NON-NLS-1$
String unbindValue = (String) value;
if ("-".equals(unbindValue)) { //$NON-NLS-1$
unbind = null;
} else {
unbind = unbindValue;
if (!errorLevel.isIgnore()) {
IMethodBinding unbindMethod = findReferenceMethod(methodBinding.getDeclaringClass(), serviceType, unbind, true);
if (unbindMethod == null) {
reportProblem(annotation, "unbind", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUnbind, unbind), unbind); //$NON-NLS-1$
}
}
}
} else {
String unbindCandidate;
if (methodName.startsWith("add")) { //$NON-NLS-1$
unbindCandidate = "remove" + methodName.substring("add".length()); //$NON-NLS-1$ //$NON-NLS-2$
} else {
unbindCandidate = "un" + methodName; //$NON-NLS-1$
}
IMethodBinding unbindMethod = findReferenceMethod(methodBinding.getDeclaringClass(), serviceType, unbindCandidate, false);
if (unbindMethod == null) {
unbind = null;
if (IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC.equals(policy)) {
reportProblem(annotation, null, missingUnbindMethodLevel, problems, NLS.bind(Messages.AnnotationProcessor_noImplicitReferenceUnbind, unbindCandidate), unbindCandidate);
}
} else {
unbind = unbindMethod.getName();
}
}
String policyOption = null;
if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyOptionBinding = (IVariableBinding) value;
policyOption = DSEnums.getReferencePolicyOption(policyOptionBinding.getName());
}
String updated;
if ((value = params.get("updated")) instanceof String) { //$NON-NLS-1$
String updatedValue = (String) value;
if ("-".equals(updatedValue)) { //$NON-NLS-1$
updated = null;
} else {
updated = updatedValue;
if (!errorLevel.isIgnore()) {
IMethodBinding updatedMethod = findReferenceMethod(methodBinding.getDeclaringClass(), serviceType, updated, true);
if (updatedMethod == null) {
reportProblem(annotation, "updated", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUpdated, updated), updated); //$NON-NLS-1$
}
}
}
} else {
String updatedCandidate;
if (methodName.startsWith("bind")) { //$NON-NLS-1$
updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("bind".length()); //$NON-NLS-1$
} else if (methodName.startsWith("set")) { //$NON-NLS-1$
updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("set".length()); //$NON-NLS-1$
} else if (methodName.startsWith("add")) { //$NON-NLS-1$
updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("add".length()); //$NON-NLS-1$
} else {
updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName;
}
IMethodBinding updatedMethod = findReferenceMethod(methodBinding.getDeclaringClass(), serviceType, updatedCandidate, false);
if (updatedMethod == null) {
updated = null;
} else {
updated = updatedMethod.getName();
}
}
String referenceScope = null;
if (specVersion == DSAnnotationVersion.V1_3) {
if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding referenceScopeBinding = (IVariableBinding) value;
referenceScope = DSEnums.getReferenceScope(referenceScopeBinding.getName());
}
if (!errorLevel.isIgnore()) {
String bind;
if ((value = params.get("bind")) instanceof String) { //$NON-NLS-1$
bind = (String) value;
reportProblem(annotation, "bind", problems, Messages.AnnotationProcessor_invalidReference_bindMethodNameMismatch, bind); //$NON-NLS-1$
}
if (params.get("field") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "field", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "field")); //$NON-NLS-1$ //$NON-NLS-2$
}
if (params.get("fieldOption") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "fieldOption", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "fieldOption")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
updateAttributes(reference, name, service, cardinality, policy, target, policyOption, referenceScope, methodName, updated, unbind, null, null, null);
DSAnnotationVersion requiredVersion;
if (reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_SCOPE) != null) {
requiredVersion = DSAnnotationVersion.V1_3;
} else if (reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION) != null
|| reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_UPDATED) != null) {
requiredVersion = DSAnnotationVersion.V1_2;
} else {
requiredVersion = DSAnnotationVersion.V1_1;
}
return requiredVersion;
}
private String getReferenceName(String methodName, Map<String, Object> params) {
Object value;
String name;
if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$
name = (String) value;
} else if (methodName.startsWith("bind")) { //$NON-NLS-1$
name = methodName.substring("bind".length()); //$NON-NLS-1$
} else if (methodName.startsWith("set")) { //$NON-NLS-1$
name = methodName.substring("set".length()); //$NON-NLS-1$
} else if (methodName.startsWith("add")) { //$NON-NLS-1$
name = methodName.substring("add".length()); //$NON-NLS-1$
} else {
name = methodName;
}
return name;
}
private void updateAttributes(
IDSReference reference,
String name,
String service,
String cardinality,
String policy,
String target,
String policyOption,
String scope,
String bind,
String updated,
String unbind,
String field,
String fieldOption,
String fieldCollectionType) {
if (name == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_NAME, null);
} else {
reference.setReferenceName(name);
}
if (service == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_INTERFACE, null);
} else {
reference.setReferenceInterface(service);
}
if (cardinality == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE);
} else {
reference.setReferenceCardinality(cardinality);
}
if (policy == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, IDSConstants.VALUE_REFERENCE_POLICY_STATIC);
} else {
reference.setReferencePolicy(policy);
}
if (target == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_TARGET, null);
} else {
reference.setReferenceTarget(target);
}
if (policyOption == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_POLICY_OPTION, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION, policyOption);
}
if (scope == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_SCOPE, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_SCOPE, scope);
}
if (bind == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_BIND, null);
} else {
reference.setReferenceBind(bind);
}
if (updated == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_UPDATED, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_UPDATED, updated);
}
if (unbind == null) {
removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_UNBIND, null);
} else {
reference.setReferenceUnbind(unbind);
}
if (field == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_FIELD, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_FIELD, field);
}
if (fieldOption == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_FIELD_OPTION, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_FIELD_OPTION, fieldOption);
}
if (fieldCollectionType == null) {
removeAttribute(reference, ATTRIBUTE_REFERENCE_FIELD_COLLECTION_TYPE, null);
} else {
reference.setXMLAttribute(ATTRIBUTE_REFERENCE_FIELD_COLLECTION_TYPE, fieldCollectionType);
}
}
private void processReference(IDSReference reference, FieldDeclaration field, IVariableBinding fieldBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, Object> params, Map<String, Annotation> names, Collection<DSAnnotationProblem> problems) {
String cardinality = null;
Object value;
if ((value = params.get("cardinality")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding cardinalityBinding = (IVariableBinding) value;
cardinality = DSEnums.getReferenceCardinality(cardinalityBinding.getName());
}
ITypeBinding fieldType = fieldBinding.getType();
FieldCollectionTypeDescriptor collectionType = null;
if (cardinality == null) {
collectionType = determineCollectionType(field.getAST(), fieldType);
if (collectionType.getElementType() == null) {
cardinality = IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE;
} else {
cardinality = IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N;
}
} else {
if (!errorLevel.isIgnore()
&& (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality))) {
collectionType = determineCollectionType(field.getAST(), fieldType);
if (collectionType.getElementType() == null) {
reportProblem(annotation, "cardinality", problems, Messages.AnnotationProcessor_invalidReference_fieldTypeCardinalityMismatch, cardinality); //$NON-NLS-1$
}
}
}
ITypeBinding serviceType;
if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$
serviceType = (ITypeBinding) value;
if (!errorLevel.isIgnore()) {
ITypeBinding targetType = fieldType;
if (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality)) {
if (collectionType == null) {
collectionType = determineCollectionType(field.getAST(), fieldType);
}
targetType = collectionType.getElementType();
}
if (targetType != null) {
String erasure = targetType.getErasure().getBinaryName();
ITypeBinding[] typeArgs;
if (!((ServiceReference.class.getName().equals(erasure) || COMPONENT_SERVICE_OBJECTS.equals(erasure))
&& ((typeArgs = targetType.getTypeArguments()).length == 0 || serviceType.isAssignmentCompatible(typeArgs[0])))
&& !Map.class.getName().equals(erasure)
&& !(Map.Entry.class.getName().equals(erasure)
&& ((typeArgs = targetType.getTypeArguments()).length < 2 || (Map.class.getName().equals(typeArgs[0].getErasure().getBinaryName()) && serviceType.isAssignmentCompatible(typeArgs[1]))))
&& !serviceType.isAssignmentCompatible(targetType)) {
reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, targetType.getName(), serviceType.getName()), targetType.getName(), serviceType.getName()); //$NON-NLS-1$
}
}
}
} else {
ITypeBinding targetType = fieldType;
if (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality)) {
if (collectionType == null) {
collectionType = determineCollectionType(field.getAST(), targetType);
}
targetType = collectionType.getElementType();
}
serviceType = targetType == null ? null : getFieldServiceType(field.getAST(), targetType);
}
if (serviceType == null) {
reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReference_fieldUnknownServiceType);
serviceType = field.getAST().resolveWellKnownType(Object.class.getName());
}
validateReferenceField(annotation, fieldBinding, problems);
String service = serviceType == null ? null : serviceType.getBinaryName();
String fieldCollectionType = null;
if (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality)) {
if (collectionType == null) {
collectionType = determineCollectionType(field.getAST(), fieldType);
}
if (collectionType.getElementType() != null) {
String erasure = collectionType.getElementType().getErasure().getBinaryName();
if (ServiceReference.class.getName().equals(erasure)) {
fieldCollectionType = "reference"; //$NON-NLS-1$
} else if (COMPONENT_SERVICE_OBJECTS.equals(erasure)) {
fieldCollectionType = "serviceobjects"; //$NON-NLS-1$
} else if (Map.class.getName().equals(erasure)) {
fieldCollectionType = "properties"; //$NON-NLS-1$
} else if (Map.Entry.class.equals(erasure)) {
fieldCollectionType = "tuple"; //$NON-NLS-1$
}
}
}
String fieldName = fieldBinding.getName();
String name;
if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$
name = (String) value;
} else {
name = fieldName;
}
if (!errorLevel.isIgnore()) {
if (names.containsKey(name)) {
reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
Annotation duplicate = names.put(name, null);
if (duplicate != null) {
reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
}
} else {
names.put(name, annotation);
}
}
if (!errorLevel.isIgnore()) {
String fieldVal;
if ((value = params.get("field")) instanceof String) { //$NON-NLS-1$
fieldVal = (String) value;
reportProblem(annotation, "field", problems, Messages.AnnotationProcessor_invalidReference_fieldNameMismatch, fieldVal); //$NON-NLS-1$
}
}
String policy = null;
if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyBinding = (IVariableBinding) value;
policy = DSEnums.getReferencePolicy(policyBinding.getName());
} else if (Modifier.isVolatile(field.getModifiers())) {
policy = IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC;
}
String target = null;
if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$
target = (String) value;
validateReferenceTarget(annotation, target, problems);
}
if (!errorLevel.isIgnore()) {
if (params.get("bind") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "bind", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "bind")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (!errorLevel.isIgnore()) {
if (params.get("unbind") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "unbind", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "unbind")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String policyOption = null;
if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyOptionBinding = (IVariableBinding) value;
policyOption = DSEnums.getReferencePolicyOption(policyOptionBinding.getName());
}
if (!errorLevel.isIgnore()) {
if (params.get("updated") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "updated", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "updated")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String referenceScope = null;
if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding referenceScopeBinding = (IVariableBinding) value;
referenceScope = DSEnums.getReferenceScope(referenceScopeBinding.getName());
}
String fieldOption = null;
if ((value = params.get("fieldOption")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding fieldOptionBinding = (IVariableBinding) value;
fieldOption = DSEnums.getFieldOption(fieldOptionBinding.getName());
if (!errorLevel.isIgnore()) {
if (VALUE_REFERENCE_FIELD_OPTION_REPLACE.equals(fieldOption)) {
if (Modifier.isFinal(field.getModifiers())) {
reportProblem(annotation, "fieldOption", problems, Messages.AnnotationProcessor_invalidReference_fieldFinal_fieldOption, fieldOption); //$NON-NLS-1$
}
} else if (VALUE_REFERENCE_FIELD_OPTION_UPDATE.equals(fieldOption)) {
if (!(IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC.equals(policy)
&& (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality)))) {
reportProblem(annotation, "fieldOption", problems, Messages.AnnotationProcessor_invalidReference_fieldPolicyCardinality_fieldOption, fieldOption); //$NON-NLS-1$
}
}
}
} else {
if (IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC.equals(policy)
&& (IDSConstants.VALUE_REFERENCE_CARDINALITY_ZERO_N.equals(cardinality)
|| IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_N.equals(cardinality))
&& Modifier.isFinal(field.getModifiers())) {
fieldOption = VALUE_REFERENCE_FIELD_OPTION_UPDATE;
}
}
if (!errorLevel.isIgnore()) {
if (collectionType != null && collectionType.getElementType() != null && !collectionType.isExact()) {
if (!IDSConstants.VALUE_REFERENCE_POLICY_DYNAMIC.equals(policy)) {
reportProblem(annotation, policy == null ? null : "policy", problems, Messages.AnnotationProcessor_invalidReference_fieldCardinalityPolicyCollectionType); //$NON-NLS-1$
}
if (!VALUE_REFERENCE_FIELD_OPTION_UPDATE.equals(fieldOption)) {
reportProblem(annotation, fieldOption == null ? null : "fieldOption", problems, Messages.AnnotationProcessor_invalidReference_fieldCollection_fieldOption); //$NON-NLS-1$
}
// TODO validate that field is initialized in constructor!
}
}
updateAttributes(reference, name, service, cardinality, policy, target, policyOption, referenceScope, null, null, null, fieldName, fieldOption, fieldCollectionType);
}
private FieldCollectionTypeDescriptor determineCollectionType(AST ast, ITypeBinding type) {
HashSet<ITypeBinding> visited = new HashSet<>();
LinkedList<ITypeBinding> types = new LinkedList<>();
boolean exact = true;
do {
if (!visited.add(type)) {
continue;
}
String erasure = type.getErasure().getBinaryName();
if (Collection.class.getName().equals(erasure) || List.class.getName().equals(erasure)) {
ITypeBinding[] typeArgs = type.getTypeArguments();
if (typeArgs.length > 0) {
return new FieldCollectionTypeDescriptor(typeArgs[0], exact);
}
return new FieldCollectionTypeDescriptor(ast.resolveWellKnownType(Object.class.getName()), exact);
}
exact = false;
ITypeBinding superType = type.getSuperclass();
if (superType != null && !superType.isEqualTo(ast.resolveWellKnownType(Object.class.getName()))) {
types.add(superType);
}
types.addAll(Arrays.asList(type.getInterfaces()));
} while ((type = types.poll()) != null);
return new FieldCollectionTypeDescriptor(null, false);
}
private ITypeBinding getFieldServiceType(AST ast, ITypeBinding type) {
ITypeBinding serviceType;
String erasure = type.getErasure().getBinaryName();
if (ServiceReference.class.getName().equals(erasure) || COMPONENT_SERVICE_OBJECTS.equals(erasure)) {
ITypeBinding[] typeArgs = type.getTypeArguments();
if (typeArgs.length > 0) {
serviceType = typeArgs[0];
} else {
serviceType = null;
}
} else if (Map.Entry.class.getName().equals(erasure)) {
ITypeBinding[] typeArgs = type.getTypeArguments();
if (typeArgs.length >= 2 && Map.class.getName().equals(typeArgs[0].getErasure().getBinaryName())) {
serviceType = typeArgs[1];
} else {
serviceType = null;
}
} else if (Map.class.getName().equals(erasure)) {
serviceType = null;
} else {
serviceType = type.isPrimitive() ? getObjectType(ast, type) : type;
}
return serviceType;
}
private ITypeBinding getObjectType(AST ast, ITypeBinding primitive) {
if (Boolean.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Boolean.class.getName());
}
if (Byte.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Byte.class.getName());
}
if (Character.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Character.class.getName());
}
if (Double.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Double.class.getName());
}
if (Float.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Float.class.getName());
}
if (Integer.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Integer.class.getName());
}
if (Long.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Long.class.getName());
}
if (Short.TYPE.getName().equals(primitive.getName())) {
return ast.resolveWellKnownType(Short.class.getName());
}
return null;
}
private void validateReferenceField(Annotation annotation, IVariableBinding fieldBinding, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
if (Modifier.isStatic(fieldBinding.getModifiers())) {
reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReference_staticField);
}
}
private void processReference(IDSReference reference, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, Object> params, Map<String, Annotation> names, Collection<DSAnnotationProblem> problems) {
ITypeBinding serviceType = null;
Object value;
if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$
serviceType = (ITypeBinding) value;
} else {
if (!errorLevel.isIgnore()) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidReference_missingRequiredParam, "service")); //$NON-NLS-1$
}
}
String service = serviceType == null ? null : serviceType.getBinaryName();
String name = null;
if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$
name = (String) value;
if (!errorLevel.isIgnore()) {
if (names.containsKey(name)) {
reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
Annotation duplicate = names.put(name, null);
if (duplicate != null) {
reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$
}
} else {
names.put(name, annotation);
}
}
} else {
if (!errorLevel.isIgnore()) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidReference_missingRequiredParam, "name")); //$NON-NLS-1$
}
}
String cardinality = null;
if ((value = params.get("cardinality")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding cardinalityBinding = (IVariableBinding) value;
cardinality = DSEnums.getReferenceCardinality(cardinalityBinding.getName());
}
String policy = null;
if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyBinding = (IVariableBinding) value;
policy = DSEnums.getReferencePolicy(policyBinding.getName());
}
String target = null;
if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$
target = (String) value;
validateReferenceTarget(annotation, target, problems);
}
if (!errorLevel.isIgnore()) {
if (params.get("bind") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "bind", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "bind")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (!errorLevel.isIgnore()) {
if (params.get("unbind") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "unbind", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "unbind")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String policyOption = null;
if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding policyOptionBinding = (IVariableBinding) value;
policyOption = DSEnums.getReferencePolicyOption(policyOptionBinding.getName());
}
if (!errorLevel.isIgnore()) {
if (params.get("updated") instanceof String) { //$NON-NLS-1$
reportProblem(annotation, "updated", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "updated")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
String referenceScope = null;
if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
IVariableBinding referenceScopeBinding = (IVariableBinding) value;
referenceScope = DSEnums.getReferenceScope(referenceScopeBinding.getName());
}
if (!errorLevel.isIgnore()) {
if (params.get("field") instanceof IVariableBinding) { //$NON-NLS-1$
reportProblem(annotation, "field", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "field")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (!errorLevel.isIgnore()) {
if (params.get("fieldOption") instanceof IVariableBinding) { //$NON-NLS-1$
reportProblem(annotation, "fieldOption", true, problems, NLS.bind(Messages.AnnotationProcessor_parameterNotApplicable, "fieldOption")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
updateAttributes(reference, name, service, cardinality, policy, target, policyOption, referenceScope, null, null, null, null, null, null);
}
private void validateReferenceBindMethod(Annotation annotation, ITypeBinding serviceType, IMethodBinding methodBinding, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
if (Modifier.isStatic(methodBinding.getModifiers())) {
reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReference_staticBindMethod);
}
String returnTypeName = methodBinding.getReturnType().getName();
if (!Void.TYPE.getName().equals(returnTypeName)) {
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodReturnType, returnTypeName), returnTypeName);
}
ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes();
String erasure;
if (!(paramTypeBindings.length == 1
&& (ServiceReference.class.getName().equals(erasure = paramTypeBindings[0].getErasure().getBinaryName())
|| (specVersion == DSAnnotationVersion.V1_3 && COMPONENT_SERVICE_OBJECTS.equals(erasure))
|| serviceType == null
|| serviceType.isAssignmentCompatible(paramTypeBindings[0])))
&& !(paramTypeBindings.length == 2
&& (serviceType == null || serviceType.isAssignmentCompatible(paramTypeBindings[0]))
&& Map.class.getName().equals(paramTypeBindings[1].getErasure().getBinaryName()))) {
String[] params = new String[paramTypeBindings.length];
StringBuilder buf = new StringBuilder(64);
buf.append('(');
for (int i = 0; i < params.length; ++i) {
params[i] = paramTypeBindings[i].getName();
if (buf.length() > 1) {
buf.append(", "); //$NON-NLS-1$
}
buf.append(params[i]);
}
buf.append(')');
reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodParameters, buf, serviceType == null ? Messages.AnnotationProcessor_unknownServiceTypeLabel : serviceType.getName()), params);
}
}
private void validateReferenceTarget(Annotation annotation, String target, Collection<DSAnnotationProblem> problems) {
if (errorLevel.isIgnore()) {
return;
}
try {
FrameworkUtil.createFilter(target);
} catch (InvalidSyntaxException e) {
String msg = e.getMessage();
String suffix = ": " + e.getFilter(); //$NON-NLS-1$
if (msg.endsWith(suffix)) {
msg = msg.substring(0, msg.length() - suffix.length());
}
reportProblem(annotation, "target", problems, msg, target); //$NON-NLS-1$
}
}
private IMethodBinding findReferenceMethod(ITypeBinding componentClass, ITypeBinding serviceType, String name, boolean recurse) {
ITypeBinding testedClass = componentClass;
IMethodBinding candidate = null;
int priority = 0;
// priority:
// 0: <assignment-compatible-type>, Map
// 1: <exact-type>, Map
// 2: <assignment-compatible-type>
// 3: <exact-type>
do {
for (IMethodBinding declaredMethod : testedClass.getDeclaredMethods()) {
if (name.equals(declaredMethod.getName())
&& !Modifier.isStatic(declaredMethod.getModifiers())
&& Void.TYPE.getName().equals(declaredMethod.getReturnType().getName())
&& (testedClass == componentClass
|| Modifier.isPublic(declaredMethod.getModifiers())
|| Modifier.isProtected(declaredMethod.getModifiers())
|| (!Modifier.isPrivate(declaredMethod.getModifiers())
&& testedClass.getPackage().isEqualTo(componentClass.getPackage())))) {
ITypeBinding[] paramTypes = declaredMethod.getParameterTypes();
if (paramTypes.length == 1) {
String erasure = paramTypes[0].getErasure().getBinaryName();
if (ServiceReference.class.getName().equals(erasure)) {
// we have the winner
return declaredMethod;
}
if (specVersion == DSAnnotationVersion.V1_3 && priority < 4 && COMPONENT_SERVICE_OBJECTS.equals(erasure)) {
priority = 4;
} else if (priority < 3 && serviceType != null && serviceType.isEqualTo(paramTypes[0])) {
priority = 3;
} else if (priority < 2 && serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0])) {
priority = 2;
} else {
continue;
}
// we have a (better) candidate
candidate = declaredMethod;
} else if (paramTypes.length == 2) {
if (priority < 1
&& serviceType != null && serviceType.isEqualTo(paramTypes[0])
&& Map.class.getName().equals(paramTypes[1].getErasure().getBinaryName())) {
priority = 1;
} else if (candidate != null
|| !(serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0]))
|| !Map.class.getName().equals(paramTypes[1].getErasure().getBinaryName())) {
continue;
}
// we have a candidate
candidate = declaredMethod;
}
}
}
} while (recurse && (testedClass = testedClass.getSuperclass()) != null);
return candidate;
}
private void reportProblem(Annotation annotation, String member, Collection<DSAnnotationProblem> problems, String message, String... args) {
reportProblem(annotation, member, -1, problems, message, args);
}
private void reportProblem(Annotation annotation, String member, boolean fullPair, Collection<DSAnnotationProblem> problems, String message, String... args) {
reportProblem(annotation, member, -1, fullPair, errorLevel, problems, message, args);
}
private void reportProblem(Annotation annotation, String member, ValidationErrorLevel errorLevel, Collection<DSAnnotationProblem> problems, String message, String... args) {
reportProblem(annotation, member, -1, errorLevel, problems, message, args);
}
private void reportProblem(Annotation annotation, String member, int valueIndex, Collection<DSAnnotationProblem> problems, String message, String... args) {
reportProblem(annotation, member, valueIndex, errorLevel, problems, message, args);
}
private void reportProblem(Annotation annotation, String member, int valueIndex, ValidationErrorLevel errorLevel, Collection<DSAnnotationProblem> problems, String message, String... args) {
reportProblem(annotation, member, valueIndex, false, errorLevel, problems, message, args);
}
private void reportProblem(Annotation annotation, String member, int valueIndex, boolean fullPair, ValidationErrorLevel errorLevel, Collection<DSAnnotationProblem> problems, String message, String... args) {
if (errorLevel.isIgnore()) {
return;
}
ASTNode element = annotation;
if (annotation.isNormalAnnotation() && member != null) {
NormalAnnotation na = (NormalAnnotation) annotation;
for (Object value : na.values()) {
MemberValuePair pair = (MemberValuePair) value;
if (member.equals(pair.getName().getIdentifier())) {
element = fullPair ? pair : pair.getValue();
break;
}
}
} else if (annotation.isSingleMemberAnnotation()) {
SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation;
element = sma.getValue();
}
int start = element.getStartPosition();
int length = element.getLength();
if (valueIndex >= 0 && element instanceof ArrayInitializer) {
ArrayInitializer ai = (ArrayInitializer) element;
if (valueIndex < ai.expressions().size()) {
Expression expression = (Expression) ai.expressions().get(valueIndex);
start = expression.getStartPosition();
length = expression.getLength();
}
}
if (start >= 0) {
DSAnnotationProblem problem = new DSAnnotationProblem(errorLevel.isError(), message, args);
problem.setSourceStart(start);
problem.setSourceEnd(start + length - 1);
problems.add(problem);
}
}
}