1 /*******************************************************************************
2  * Copyright (c) 2017, 2018 Ecliptical Software Inc. and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Ecliptical Software Inc. - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.ds.internal.annotations;
15 
16 import java.io.BufferedReader;
17 import java.io.ByteArrayInputStream;
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.StringReader;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.LinkedHashMap;
32 import java.util.LinkedHashSet;
33 import java.util.List;
34 import java.util.ListIterator;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.concurrent.CountDownLatch;
38 import java.util.regex.Pattern;
39 
40 import org.eclipse.core.filebuffers.FileBuffers;
41 import org.eclipse.core.filebuffers.ITextFileBuffer;
42 import org.eclipse.core.filebuffers.ITextFileBufferManager;
43 import org.eclipse.core.filebuffers.LocationKind;
44 import org.eclipse.core.resources.IFile;
45 import org.eclipse.core.resources.IProject;
46 import org.eclipse.core.runtime.CoreException;
47 import org.eclipse.core.runtime.IPath;
48 import org.eclipse.core.runtime.IStatus;
49 import org.eclipse.core.runtime.Path;
50 import org.eclipse.core.runtime.Status;
51 import org.eclipse.jdt.core.IType;
52 import org.eclipse.jdt.core.dom.ASTNode;
53 import org.eclipse.jdt.core.dom.ASTVisitor;
54 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
55 import org.eclipse.jdt.core.dom.Annotation;
56 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
57 import org.eclipse.jdt.core.dom.ArrayInitializer;
58 import org.eclipse.jdt.core.dom.EnumDeclaration;
59 import org.eclipse.jdt.core.dom.Expression;
60 import org.eclipse.jdt.core.dom.FieldDeclaration;
61 import org.eclipse.jdt.core.dom.IAnnotationBinding;
62 import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
63 import org.eclipse.jdt.core.dom.IMethodBinding;
64 import org.eclipse.jdt.core.dom.ITypeBinding;
65 import org.eclipse.jdt.core.dom.IVariableBinding;
66 import org.eclipse.jdt.core.dom.MemberValuePair;
67 import org.eclipse.jdt.core.dom.MethodDeclaration;
68 import org.eclipse.jdt.core.dom.Modifier;
69 import org.eclipse.jdt.core.dom.NormalAnnotation;
70 import org.eclipse.jdt.core.dom.TypeDeclaration;
71 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
72 import org.eclipse.jface.text.BadLocationException;
73 import org.eclipse.jface.text.DocumentRewriteSession;
74 import org.eclipse.jface.text.DocumentRewriteSessionType;
75 import org.eclipse.jface.text.IDocument;
76 import org.eclipse.jface.text.IDocumentExtension4;
77 import org.eclipse.jface.text.link.LinkedModeModel;
78 import org.eclipse.osgi.util.NLS;
79 import org.eclipse.pde.core.IModelChangedEvent;
80 import org.eclipse.pde.core.ModelChangedEvent;
81 import org.eclipse.pde.internal.core.project.PDEProject;
82 import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
83 import org.eclipse.pde.internal.core.text.IDocumentElementNode;
84 import org.eclipse.pde.internal.core.text.IDocumentObject;
85 import org.eclipse.pde.internal.core.text.IDocumentTextNode;
86 import org.eclipse.pde.internal.core.text.IModelTextChangeListener;
87 import org.eclipse.pde.internal.ds.core.IDSComponent;
88 import org.eclipse.pde.internal.ds.core.IDSConstants;
89 import org.eclipse.pde.internal.ds.core.IDSDocumentFactory;
90 import org.eclipse.pde.internal.ds.core.IDSImplementation;
91 import org.eclipse.pde.internal.ds.core.IDSModel;
92 import org.eclipse.pde.internal.ds.core.IDSObject;
93 import org.eclipse.pde.internal.ds.core.IDSProperties;
94 import org.eclipse.pde.internal.ds.core.IDSProperty;
95 import org.eclipse.pde.internal.ds.core.IDSProvide;
96 import org.eclipse.pde.internal.ds.core.IDSReference;
97 import org.eclipse.pde.internal.ds.core.IDSService;
98 import org.eclipse.pde.internal.ds.core.text.DSModel;
99 import org.eclipse.text.edits.MalformedTreeException;
100 import org.eclipse.text.edits.MultiTextEdit;
101 import org.eclipse.text.edits.ReplaceEdit;
102 import org.eclipse.text.edits.TextEdit;
103 import org.osgi.framework.BundleContext;
104 
105 @SuppressWarnings("restriction")
106 public class AnnotationVisitor extends ASTVisitor {
107 
108 	private static final String COMPONENT_CONTEXT = "org.osgi.service.component.ComponentContext"; //$NON-NLS-1$
109 
110 	private static final String COMPONENT_ANNOTATION = DSAnnotationCompilationParticipant.COMPONENT_ANNOTATION;
111 
112 	private static final String ACTIVATE_ANNOTATION = "org.osgi.service.component.annotations.Activate"; //$NON-NLS-1$
113 
114 	private static final String MODIFIED_ANNOTATION = "org.osgi.service.component.annotations.Modified"; //$NON-NLS-1$
115 
116 	private static final String DEACTIVATE_ANNOTATION = "org.osgi.service.component.annotations.Deactivate"; //$NON-NLS-1$
117 
118 	private static final String REFERENCE_ANNOTATION = "org.osgi.service.component.annotations.Reference"; //$NON-NLS-1$
119 
120 	private static final String DESIGNATE_ANNOTATION = "org.osgi.service.metatype.annotations.Designate"; //$NON-NLS-1$
121 
122 	private static final Pattern PID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*"); //$NON-NLS-1$
123 
124 	private static final String ATTRIBUTE_COMPONENT_CONFIGURATION_PID = "configuration-pid"; //$NON-NLS-1$
125 
126 	private static final String ATTRIBUTE_COMPONENT_REFERENCE = "reference"; //$NON-NLS-1$
127 
128 	private static final String ATTRIBUTE_SERVICE_SCOPE = "scope"; //$NON-NLS-1$
129 
130 	private static final String VALUE_SERVICE_SCOPE_DEFAULT = DSEnums.getServiceScope("DEFAULT"); //$NON-NLS-1$
131 
132 	private static final String VALUE_SERVICE_SCOPE_SINGLETON = DSEnums.getServiceScope("SINGLETON"); //$NON-NLS-1$
133 
134 	private static final String VALUE_SERVICE_SCOPE_BUNDLE = DSEnums.getServiceScope("BUNDLE"); //$NON-NLS-1$
135 
136 	private static final Set<String> PROPERTY_TYPES = Collections.unmodifiableSet(
137 			new HashSet<>(
138 					Arrays.asList(
139 							null,
140 							IDSConstants.VALUE_PROPERTY_TYPE_STRING,
141 							IDSConstants.VALUE_PROPERTY_TYPE_LONG,
142 							IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE,
143 							IDSConstants.VALUE_PROPERTY_TYPE_FLOAT,
144 							IDSConstants.VALUE_PROPERTY_TYPE_INTEGER,
145 							IDSConstants.VALUE_PROPERTY_TYPE_BYTE,
146 							IDSConstants.VALUE_PROPERTY_TYPE_CHAR,
147 							IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN,
148 							IDSConstants.VALUE_PROPERTY_TYPE_SHORT)));
149 
150 	private static final Map<String, String> PRIMITIVE_TYPE_MAP;
151 
152 	static {
153 		HashMap<String, String> map = new HashMap<>(16);
Long.class.getName()154 		map.put(Long.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_LONG);
Double.class.getName()155 		map.put(Double.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE);
Float.class.getName()156 		map.put(Float.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_FLOAT);
Integer.class.getName()157 		map.put(Integer.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_INTEGER);
Byte.class.getName()158 		map.put(Byte.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BYTE);
Character.class.getName()159 		map.put(Character.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_CHAR);
Boolean.class.getName()160 		map.put(Boolean.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
Short.class.getName()161 		map.put(Short.class.getName(), IDSConstants.VALUE_PROPERTY_TYPE_SHORT);
Long.TYPE.getName()162 		map.put(Long.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_LONG);
Double.TYPE.getName()163 		map.put(Double.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE);
Float.TYPE.getName()164 		map.put(Float.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_FLOAT);
Integer.TYPE.getName()165 		map.put(Integer.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_INTEGER);
Byte.TYPE.getName()166 		map.put(Byte.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BYTE);
Character.TYPE.getName()167 		map.put(Character.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_CHAR);
Boolean.TYPE.getName()168 		map.put(Boolean.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN);
Short.TYPE.getName()169 		map.put(Short.TYPE.getName(), IDSConstants.VALUE_PROPERTY_TYPE_SHORT);
170 		PRIMITIVE_TYPE_MAP = Collections.unmodifiableMap(map);
171 	}
172 
173 	private static final Comparator<IDSReference> REF_NAME_COMPARATOR = (o1, o2) -> o1.getReferenceName()
174 			.compareTo(o2.getReferenceName());
175 
176 	private static final Debug debug = AnnotationProcessor.debug;
177 
178 	private final AnnotationProcessor processor;
179 
180 	private final ProjectState state;
181 
182 	private final DSAnnotationVersion specVersion;
183 
184 	private final ValidationErrorLevel errorLevel;
185 
186 	private final Map<String, String> dsKeys;
187 
188 	private final ProblemReporter problemReporter;
189 
AnnotationVisitor(AnnotationProcessor processor, ProjectState state, Map<String, String> dsKeys, Set<DSAnnotationProblem> problems)190 	public AnnotationVisitor(AnnotationProcessor processor, ProjectState state, Map<String, String> dsKeys, Set<DSAnnotationProblem> problems) {
191 		this.processor = processor;
192 		this.state = state;
193 		this.specVersion = state.getSpecVersion();
194 		this.errorLevel = state.getErrorLevel();
195 		this.dsKeys = dsKeys;
196 		problemReporter = new ProblemReporter(state.getErrorLevel(), problems);
197 	}
198 
199 	@Override
visit(TypeDeclaration type)200 	public boolean visit(TypeDeclaration type) {
201 		if (!Modifier.isPublic(type.getModifiers())) {
202 			// non-public types cannot be (or have nested) components
203 			if (errorLevel.isIgnore()) {
204 				return false;
205 			}
206 
207 			Annotation annotation = findComponentAnnotation(type);
208 			if (annotation != null) {
209 				problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notPublic, type.getName().getIdentifier()), type.getName().getIdentifier());
210 			}
211 
212 			return true;
213 		}
214 
215 		Annotation annotation = findComponentAnnotation(type);
216 		if (annotation != null) {
217 			boolean isInterface = false;
218 			boolean isAbstract = false;
219 			boolean isNested = false;
220 			boolean noDefaultConstructor = false;
221 			if ((isInterface = type.isInterface())
222 					|| (isAbstract = Modifier.isAbstract(type.getModifiers()))
223 					|| (isNested = (!type.isPackageMemberTypeDeclaration() && !isNestedPublicStatic(type)))
224 					|| (noDefaultConstructor = !hasDefaultConstructor(type))) {
225 				// interfaces, abstract types, non-static/non-public nested types, or types with no default constructor cannot be components
226 				if (!errorLevel.isIgnore()) {
227 					if (isInterface) {
228 						problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_interface, type.getName().getIdentifier()), type.getName().getIdentifier());
229 					} else if (isAbstract) {
230 						problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_abstract, type.getName().getIdentifier()), type.getName().getIdentifier());
231 					} else if (isNested) {
232 						problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notTopLevel, type.getName().getIdentifier()), type.getName().getIdentifier());
233 					} else if (noDefaultConstructor) {
234 						problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor, type.getName().getIdentifier()), type.getName().getIdentifier());
235 					} else {
236 						problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidComponentImplementationClass, type.getName().getIdentifier()), type.getName().getIdentifier());
237 					}
238 				}
239 			} else {
240 				ITypeBinding typeBinding = type.resolveBinding();
241 				if (typeBinding == null) {
242 					if (debug.isDebugging()) {
243 						debug.trace(String.format("Unable to resolve binding for type: %s", type)); //$NON-NLS-1$
244 					}
245 				} else {
246 					IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
247 					if (annotationBinding == null) {
248 						if (debug.isDebugging()) {
249 							debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$
250 						}
251 					} else {
252 						try {
253 							processComponent(type, typeBinding, annotation, annotationBinding);
254 						} catch (CoreException e) {
255 							Activator.log(e);
256 						}
257 					}
258 				}
259 			}
260 		}
261 
262 		return true;
263 	}
264 
265 	@Override
visit(EnumDeclaration node)266 	public boolean visit(EnumDeclaration node) {
267 		Annotation annotation = findComponentAnnotation(node);
268 		if (annotation != null) {
269 			problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_enumeration, node.getName().getIdentifier()), node.getName().getIdentifier());
270 		}
271 
272 		return false;
273 	}
274 
275 	@Override
visit(AnnotationTypeDeclaration node)276 	public boolean visit(AnnotationTypeDeclaration node) {
277 		Annotation annotation = findComponentAnnotation(node);
278 		if (annotation != null) {
279 			problemReporter.reportProblem(annotation, null, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_annotation, node.getName().getIdentifier()), node.getName().getIdentifier());
280 		}
281 
282 		return true;
283 	}
284 
findComponentAnnotation(AbstractTypeDeclaration type)285 	private Annotation findComponentAnnotation(AbstractTypeDeclaration type) {
286 		for (Object item : type.modifiers()) {
287 			if (!(item instanceof Annotation)) {
288 				continue;
289 			}
290 
291 			Annotation annotation = (Annotation) item;
292 			IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
293 			if (annotationBinding == null) {
294 				if (debug.isDebugging()) {
295 					debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$
296 				}
297 
298 				continue;
299 			}
300 
301 			if (COMPONENT_ANNOTATION.equals(annotationBinding.getAnnotationType().getQualifiedName())) {
302 				return annotation;
303 			}
304 		}
305 
306 		return null;
307 	}
308 
isNestedPublicStatic(AbstractTypeDeclaration type)309 	private boolean isNestedPublicStatic(AbstractTypeDeclaration type) {
310 		if (Modifier.isStatic(type.getModifiers())) {
311 			ASTNode parent = type.getParent();
312 			if (parent != null && (parent.getNodeType() == ASTNode.TYPE_DECLARATION || parent.getNodeType() == ASTNode.ANNOTATION_TYPE_DECLARATION)) {
313 				AbstractTypeDeclaration parentType = (AbstractTypeDeclaration) parent;
314 				if (Modifier.isPublic(parentType.getModifiers())) {
315 					return parentType.isPackageMemberTypeDeclaration() || isNestedPublicStatic(parentType);
316 				}
317 			}
318 		}
319 
320 		return false;
321 	}
322 
hasDefaultConstructor(TypeDeclaration type)323 	private boolean hasDefaultConstructor(TypeDeclaration type) {
324 		boolean hasConstructor = false;
325 		for (MethodDeclaration method : type.getMethods()) {
326 			if (method.isConstructor()) {
327 				hasConstructor = true;
328 				if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) {
329 					return true;
330 				}
331 			}
332 		}
333 
334 		return !hasConstructor;
335 	}
336 
processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding)337 	private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding) throws CoreException {
338 		// determine component name
339 		HashMap<String, Object> params = new HashMap<>();
340 		for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) {
341 			params.put(pair.getName(), pair.getValue());
342 		}
343 
344 		String implClass = typeBinding.getBinaryName();
345 
346 		String name = implClass;
347 		Object value;
348 		if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$
349 			name = (String) value;
350 			validateComponentName(annotation, name);
351 		}
352 
353 		// set up document to edit
354 		IPath path = new Path(state.getPath()).append(name).addFileExtension("xml"); //$NON-NLS-1$
355 
356 		String dsKey = path.toPortableString();
357 		dsKeys.put(implClass, dsKey);
358 
359 		IProject project = typeBinding.getJavaElement().getJavaProject().getProject();
360 		IFile file = PDEProject.getBundleRelativeFile(project, path);
361 		IPath filePath = file.getFullPath();
362 
363 		processor.verifyOutputLocation(file);
364 
365 		// handle file move/rename
366 		String oldPath = state.getModelFile(implClass);
367 		if (oldPath != null && !oldPath.equals(dsKey) && !file.exists()) {
368 			IFile oldFile = PDEProject.getBundleRelativeFile(project, Path.fromPortableString(oldPath));
369 			if (oldFile.exists()) {
370 				try {
371 					oldFile.move(file.getFullPath(), true, true, null);
372 				} catch (CoreException e) {
373 					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$
374 				}
375 			}
376 		}
377 
378 		ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
379 		bufferManager.connect(filePath, LocationKind.IFILE, null);
380 		ITextFileBuffer buffer = bufferManager.getTextFileBuffer(filePath, LocationKind.IFILE);
381 		if (buffer.isDirty()) {
382 			buffer.commit(null, true);
383 		}
384 
385 		IDocument document = buffer.getDocument();
386 
387 		final DSModel dsModel = new DSModel(document, true);
388 		dsModel.setUnderlyingResource(file);
389 		dsModel.setCharset(StandardCharsets.UTF_8); // $NON-NLS-1$
390 		dsModel.load();
391 
392 		// note: we can't use XMLTextChangeListener because it generates overlapping edits!
393 		// thus we replace the entire content with one edit (if changed)
394 		final IDocument fDoc = document;
395 		dsModel.addModelChangedListener(new IModelTextChangeListener() {
396 
397 			private final IDocument document = fDoc;
398 
399 			private boolean changed;
400 
401 			@Override
402 			public void modelChanged(IModelChangedEvent event) {
403 				changed = true;
404 			}
405 
406 			@Override
407 			public TextEdit[] getTextOperations() {
408 				if (!changed) {
409 					return new TextEdit[0];
410 				}
411 
412 				String text = dsModel.getContents();
413 				ReplaceEdit edit = new ReplaceEdit(0, document.getLength(), text);
414 				return new TextEdit[] { edit };
415 			}
416 
417 			@Override
418 			public String getReadableName(TextEdit edit) {
419 				return null;
420 			}
421 		});
422 
423 		try {
424 			processComponent(dsModel, type, typeBinding, annotation, annotationBinding, params, name, implClass);
425 
426 			TextEdit[] edits = dsModel.getLastTextChangeListener().getTextOperations();
427 			if (edits.length > 0) {
428 				if (debug.isDebugging()) {
429 					debug.trace(String.format("Saving model: %s", file.getFullPath())); //$NON-NLS-1$
430 				}
431 
432 				final MultiTextEdit edit = new MultiTextEdit();
433 				edit.addChildren(edits);
434 
435 				if (buffer.isSynchronizationContextRequested()) {
436 					final IDocument doc = document;
437 					final CoreException[] ex = new CoreException[1];
438 					final CountDownLatch latch = new CountDownLatch(1);
439 					bufferManager.execute(() -> {
440 						try {
441 							performEdit(doc, edit);
442 						} catch (CoreException e) {
443 							ex[0] = e;
444 						}
445 
446 						latch.countDown();
447 					});
448 
449 					try {
450 						latch.await();
451 					} catch (InterruptedException e) {
452 						if (debug.isDebugging())
453 							debug.trace("Interrupted while waiting for edits to complete on display thread.", e); //$NON-NLS-1$
454 					}
455 
456 					if (ex[0] != null) {
457 						throw ex[0];
458 					}
459 				} else {
460 					performEdit(document, edit);
461 				}
462 
463 				buffer.commit(null, true);
464 			}
465 		} finally {
466 			dsModel.dispose();
467 			bufferManager.disconnect(buffer.getLocation(), LocationKind.IFILE, null);
468 		}
469 	}
470 
performEdit(IDocument document, TextEdit edit)471 	private void performEdit(IDocument document, TextEdit edit) throws CoreException {
472 		DocumentRewriteSession session = null;
473 		try {
474 			if (document instanceof IDocumentExtension4) {
475 				session = ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
476 			}
477 
478 			LinkedModeModel.closeAllModels(document);
479 			edit.apply(document);
480 		} catch (MalformedTreeException | BadLocationException e) {
481 			throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$
482 		} finally {
483 			if (session != null) {
484 				((IDocumentExtension4) document).stopRewriteSession(session);
485 			}
486 		}
487 	}
488 
processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, ?> params, String name, String implClass)489 	private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map<String, ?> params, String name, String implClass) {
490 		Object value;
491 		Collection<String> services;
492 		if ((value = params.get("service")) instanceof Object[]) { //$NON-NLS-1$
493 			Object[] elements = (Object[]) value;
494 			services = new LinkedHashSet<>(elements.length);
495 			Map<String, Integer> serviceDuplicates = errorLevel.isIgnore() ? null : new HashMap<>();
496 			for (int i = 0; i < elements.length; ++i) {
497 				ITypeBinding serviceType = (ITypeBinding) elements[i];
498 				String serviceName = serviceType.getBinaryName();
499 				if (services.add(serviceName)) {
500 					if (serviceDuplicates != null) {
501 						serviceDuplicates.put(serviceName, i);
502 					}
503 				} else {
504 					if (serviceDuplicates != null) {
505 						problemReporter.reportProblem(annotation, "service", i, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$
506 						Integer pos = serviceDuplicates.put(serviceName, null);
507 						if (pos != null) {
508 							problemReporter.reportProblem(annotation, "service", pos.intValue(), Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$
509 						}
510 					}
511 				}
512 
513 				validateComponentService(annotation, typeBinding, serviceType, i);
514 			}
515 		} else {
516 			ITypeBinding[] serviceTypes = typeBinding.getInterfaces();
517 			services = new ArrayList<>(serviceTypes.length);
518 			for (ITypeBinding serviceType : serviceTypes) {
519 				services.add(serviceType.getBinaryName());
520 			}
521 		}
522 
523 		String factory = null;
524 		if ((value = params.get("factory")) instanceof String) { //$NON-NLS-1$
525 			factory = (String) value;
526 			validateComponentFactory(annotation, factory);
527 		}
528 
529 		Boolean serviceFactory = null;
530 		if ((value = params.get("servicefactory")) instanceof Boolean) { //$NON-NLS-1$
531 			serviceFactory = (Boolean) value;
532 			if (!errorLevel.isIgnore() && Boolean.TRUE.equals(serviceFactory) && services.isEmpty()) {
533 				problemReporter.reportProblem(annotation, "servicefactory", Messages.AnnotationVisitor_invalidServiceFactory_noServices); //$NON-NLS-1$
534 			}
535 		}
536 
537 		Boolean enabled = null;
538 		if ((value = params.get("enabled")) instanceof Boolean) { //$NON-NLS-1$
539 			enabled = (Boolean) value;
540 		}
541 
542 		Boolean immediate = null;
543 		if ((value = params.get("immediate")) instanceof Boolean) { //$NON-NLS-1$
544 			immediate = (Boolean) value;
545 			if (!errorLevel.isIgnore()) {
546 				if (factory != null && Boolean.TRUE.equals(immediate)) {
547 					problemReporter.reportProblem(annotation, "immediate", Messages.AnnotationVisitor_invalidFactoryComponent_immediate); //$NON-NLS-1$
548 				}
549 
550 				if (services.isEmpty() && Boolean.FALSE.equals(immediate)) {
551 					problemReporter.reportProblem(annotation, "immediate", Messages.AnnotationVisitor_invalidDelayedComponent_noServices); //$NON-NLS-1$
552 				}
553 			}
554 		}
555 
556 		String[] properties;
557 		if ((value = params.get("property")) instanceof Object[]) { //$NON-NLS-1$
558 			Object[] elements = (Object[]) value;
559 			ArrayList<String> list = new ArrayList<>(elements.length);
560 			for (Object element : elements) {
561 				if (element instanceof String) {
562 					list.add((String) element);
563 				}
564 			}
565 
566 			properties = list.toArray(new String[list.size()]);
567 		} else {
568 			properties = new String[0];
569 		}
570 
571 		String[] propertyFiles;
572 		if ((value = params.get("properties")) instanceof Object[]) { //$NON-NLS-1$
573 			Object[] elements = (Object[]) value;
574 			ArrayList<String> list = new ArrayList<>(elements.length);
575 			for (Object element : elements) {
576 				if (element instanceof String) {
577 					list.add((String) element);
578 				}
579 			}
580 
581 			propertyFiles = list.toArray(new String[list.size()]);
582 			validateComponentPropertyFiles(annotation, ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles);
583 		} else {
584 			propertyFiles = new String[0];
585 		}
586 
587 		String configPolicy = null;
588 		if ((value = params.get("configurationPolicy")) instanceof IVariableBinding) { //$NON-NLS-1$
589 			IVariableBinding configPolicyBinding = (IVariableBinding) value;
590 			configPolicy = DSEnums.getConfigurationPolicy(configPolicyBinding.getName());
591 		} else if (specVersion == DSAnnotationVersion.V1_3) {
592 			for (IAnnotationBinding typeAnnotation : typeBinding.getAnnotations()) {
593 				if (!DESIGNATE_ANNOTATION.equals(typeAnnotation.getAnnotationType().getQualifiedName())) {
594 					continue;
595 				}
596 
597 				for (IMemberValuePairBinding memberValuePair : typeAnnotation.getDeclaredMemberValuePairs()) {
598 					if (!"factory".equals(memberValuePair.getName())) { //$NON-NLS-1$
599 						continue;
600 					}
601 
602 					if (Boolean.TRUE.equals(memberValuePair.getValue())) {
603 						configPolicy = IDSConstants.VALUE_CONFIGURATION_POLICY_REQUIRE;
604 					}
605 
606 					break;
607 				}
608 
609 				break;
610 			}
611 		}
612 
613 		DSAnnotationVersion requiredVersion = DSAnnotationVersion.V1_1;
614 
615 		String configPid = null;
616 		if ((value = params.get("configurationPid")) instanceof String) { //$NON-NLS-1$
617 			configPid = (String) value;
618 			validateComponentConfigPID(annotation, configPid, -1);
619 			requiredVersion = DSAnnotationVersion.V1_2;
620 		} else if (specVersion == DSAnnotationVersion.V1_3 && value instanceof Object[]) {
621 			Object[] configPidElems = (Object[]) value;
622 			if (configPidElems.length > 0) {
623 				LinkedHashSet<String> configPids = new LinkedHashSet<>(configPidElems.length);
624 				HashMap<String, Integer> pidDuplicates = errorLevel.isIgnore() ? null : new HashMap<>(configPidElems.length);
625 				int i = 0;
626 				for (Object configPidElem : configPidElems) {
627 					String configPidStr = String.valueOf(configPidElem);
628 					if ("$".equals(configPidStr)) { //$NON-NLS-1$
629 						configPidStr = name;
630 					} else {
631 						validateComponentConfigPID(annotation, configPidStr, i);
632 					}
633 
634 					if (configPids.add(configPidStr)) {
635 						if (pidDuplicates != null) {
636 							pidDuplicates.put(configPidStr, i);
637 						}
638 					} else {
639 						if (pidDuplicates != null) {
640 							problemReporter.reportProblem(annotation, "configurationPid", i, Messages.AnnotationVisitor_invalidComponentConfigurationPid_duplicate); //$NON-NLS-1$
641 							Integer pos = pidDuplicates.put(configPidStr, null);
642 							if (pos != null) {
643 								problemReporter.reportProblem(annotation, "configurationPid", pos.intValue(), Messages.AnnotationVisitor_invalidComponentConfigurationPid_duplicate); //$NON-NLS-1$
644 							}
645 						}
646 					}
647 
648 					i++;
649 				}
650 
651 				requiredVersion = i > 1 ?  DSAnnotationVersion.V1_3 : DSAnnotationVersion.V1_2;
652 
653 				StringBuilder configPidBuf = new StringBuilder();
654 				for (String configPidElem : configPids) {
655 					if (configPidBuf.length() > 0) {
656 						configPidBuf.append(' ');
657 					}
658 
659 					configPidBuf.append(configPidElem);
660 				}
661 
662 				configPid = configPidBuf.toString();
663 			}
664 		}
665 
666 		String serviceScope = null;
667 		if (specVersion == DSAnnotationVersion.V1_3 && (value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$
668 			IVariableBinding serviceScopeBinding = (IVariableBinding) value;
669 			serviceScope = DSEnums.getServiceScope(serviceScopeBinding.getName());
670 			if (!errorLevel.isIgnore()) {
671 				if (services.isEmpty()) {
672 					problemReporter.reportProblem(annotation, "scope", Messages.AnnotationVisitor_invalidScope_noServices); //$NON-NLS-1$
673 				} else if ((factory != null || Boolean.TRUE.equals(immediate)) && !serviceScope.equals(VALUE_SERVICE_SCOPE_SINGLETON)) {
674 					problemReporter.reportProblem(annotation, "scope", Messages.AnnotationVisitor_invalidScope_factoryImmediate); //$NON-NLS-1$
675 				}
676 			}
677 		}
678 
679 		if (specVersion == DSAnnotationVersion.V1_3 && serviceFactory != null && serviceScope != null && !serviceScope.equals(VALUE_SERVICE_SCOPE_DEFAULT)) {
680 			// ignore servicefactory if scope specified and not <<DEFAULT>>
681 			if (!errorLevel.isIgnore() && !serviceFactory.equals(VALUE_SERVICE_SCOPE_BUNDLE.equals(serviceScope))) {
682 				problemReporter.reportProblem(annotation, "servicefactory", -1, true, errorLevel, Messages.AnnotationVisitor_invalidServiceFactory_ignored); //$NON-NLS-1$
683 			}
684 
685 			serviceFactory = null;
686 		}
687 
688 		if (!errorLevel.isIgnore() && serviceFactory != null && !serviceFactory.equals(Boolean.FALSE) && !services.isEmpty()) {
689 			if (factory != null || Boolean.TRUE.equals(immediate)) {
690 				problemReporter.reportProblem(annotation, "servicefactory", Messages.AnnotationVisitor_invalidServiceFactory_factoryImmediate); //$NON-NLS-1$
691 			}
692 		}
693 
694 		IDSComponent component = model.getDSComponent();
695 
696 		if (enabled == null) {
697 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ENABLED, IDSConstants.VALUE_TRUE);
698 		} else {
699 			component.setEnabled(enabled.booleanValue());
700 		}
701 
702 		if (name == null) {
703 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_NAME, null);
704 		} else {
705 			component.setAttributeName(name);
706 		}
707 
708 		if (factory == null) {
709 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_FACTORY, null);
710 		} else {
711 			component.setFactory(factory);
712 		}
713 
714 		if (immediate == null) {
715 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_IMMEDIATE, null);
716 		} else {
717 			component.setImmediate(immediate.booleanValue());
718 		}
719 
720 		if (configPolicy == null) {
721 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_CONFIGURATION_POLICY, IDSConstants.VALUE_CONFIGURATION_POLICY_OPTIONAL);
722 		} else {
723 			component.setConfigurationPolicy(configPolicy);
724 		}
725 
726 		if (configPid == null) {
727 			removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null);
728 		} else {
729 			component.setXMLAttribute(ATTRIBUTE_COMPONENT_CONFIGURATION_PID, configPid);
730 		}
731 
732 		IDSDocumentFactory dsFactory = model.getFactory();
733 
734 		IDSService service = component.getService();
735 		if (services.isEmpty()) {
736 			if (service != null) {
737 				component.removeService(service);
738 			}
739 		} else {
740 			if (service == null) {
741 				service = dsFactory.createService();
742 
743 				// insert service element after last property or properties element
744 				int firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
745 				component.addChildNode(service, firstPos, true);
746 			}
747 
748 			if (serviceScope == null || serviceScope.equals(VALUE_SERVICE_SCOPE_DEFAULT)) {
749 				removeAttribute(service, "scope", null); //$NON-NLS-1$
750 			} else {
751 				service.setXMLAttribute(ATTRIBUTE_SERVICE_SCOPE, serviceScope);
752 				requiredVersion = DSAnnotationVersion.V1_3;
753 			}
754 
755 			IDSProvide[] provides = service.getProvidedServices();
756 			HashMap<String, IDSProvide> provideMap = new HashMap<>(provides.length);
757 			for (IDSProvide provide : provides) {
758 				provideMap.put(provide.getInterface(), provide);
759 			}
760 
761 			ArrayList<IDSProvide> provideList = new ArrayList<>(services.size());
762 			for (String serviceName : services) {
763 				IDSProvide provide = provideMap.remove(serviceName);
764 				if (provide == null) {
765 					provide = dsFactory.createProvide();
766 					provide.setInterface(serviceName);
767 				}
768 
769 				provideList.add(provide);
770 			}
771 
772 			int firstPos = provides.length == 0 ? -1 : service.indexOf(provides[0]);
773 			removeChildren(service, (provideMap.values()));
774 
775 			addOrMoveChildren(service, provideList, firstPos);
776 
777 			if (serviceFactory == null) {
778 				removeAttribute(service, IDSConstants.ATTRIBUTE_SERVICE_FACTORY, IDSConstants.VALUE_FALSE);
779 			} else {
780 				service.setServiceFactory(serviceFactory.booleanValue());
781 			}
782 		}
783 
784 		ArrayList<IDSReference> references = new ArrayList<>();
785 		HashMap<String, Annotation> referenceNames = new HashMap<>();
786 		IDSReference[] refElements = component.getReferences();
787 
788 		HashMap<String, IDSReference> refMap = new HashMap<>(refElements.length);
789 		for (IDSReference refElement : refElements) {
790 			String referenceName = refElement.getReferenceName();
791 			if (referenceName == null) {
792 				String referenceBind = refElement.getXMLAttributeValue(ReferenceProcessor.ATTRIBUTE_REFERENCE_FIELD);
793 				if (referenceBind != null) {
794 					referenceName = ReferenceProcessor.getReferenceName(referenceBind);
795 				}
796 
797 				if (referenceName == null) {
798 					referenceName = refElement.getReferenceBind();
799 					if (referenceName == null) {
800 						referenceName = refElement.getReferenceInterface();
801 					}
802 				}
803 			}
804 
805 			refMap.put(referenceName, refElement);
806 		}
807 
808 		if (annotation.isNormalAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
809 			for (Object annotationValue : ((NormalAnnotation) annotation).values()) {
810 				MemberValuePair annotationMemberValuePair = (MemberValuePair) annotationValue;
811 				if (!ATTRIBUTE_COMPONENT_REFERENCE.equals(annotationMemberValuePair.getName().getIdentifier())) {
812 					continue;
813 				}
814 
815 				ArrayList<Annotation> annotations = new ArrayList<>();
816 
817 				Expression memberValue = annotationMemberValuePair.getValue();
818 				if (memberValue instanceof Annotation) {
819 					annotations.add((Annotation) memberValue);
820 				} else if (memberValue instanceof ArrayInitializer) {
821 					for (Object memberValueElement : ((ArrayInitializer) memberValue).expressions()) {
822 						if (memberValueElement instanceof Annotation) {
823 							annotations.add((Annotation) memberValueElement);
824 						}
825 					}
826 				}
827 
828 				for (Annotation referenceAnnotation : annotations) {
829 					IAnnotationBinding referenceAnnotationBinding = referenceAnnotation.resolveAnnotationBinding();
830 					if (referenceAnnotationBinding == null) {
831 						if (debug.isDebugging()) {
832 							debug.trace(String.format("Unable to resolve binding for annotation: %s", referenceAnnotation)); //$NON-NLS-1$
833 						}
834 
835 						continue;
836 					}
837 
838 					String annotationName = referenceAnnotationBinding.getAnnotationType().getQualifiedName();
839 					if (!REFERENCE_ANNOTATION.equals(annotationName)) {
840 						continue;
841 					}
842 
843 					HashMap<String, Object> annotationParams = new HashMap<>();
844 					for (IMemberValuePairBinding pair : referenceAnnotationBinding.getDeclaredMemberValuePairs()) {
845 						annotationParams.put(pair.getName(), pair.getValue());
846 					}
847 
848 					String referenceName = (String) annotationParams.get(IDSConstants.ATTRIBUTE_REFERENCE_NAME);
849 
850 					IDSReference reference = refMap.remove(referenceName);
851 					if (reference == null) {
852 						reference = createReference(dsFactory);
853 					}
854 
855 					references.add(reference);
856 
857 					ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
858 					requiredVersion = requiredVersion.max(referenceProcessor.processReference(reference, typeBinding, referenceAnnotation, referenceAnnotationBinding, annotationParams, referenceNames));
859 				}
860 			}
861 		}
862 
863 		if (specVersion == DSAnnotationVersion.V1_3) {
864 			for (FieldDeclaration field : type.getFields()) {
865 				for (Object modifier : field.modifiers()) {
866 					if (!(modifier instanceof Annotation)) {
867 						continue;
868 					}
869 
870 					Annotation fieldAnnotation = (Annotation) modifier;
871 					IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding();
872 					if (fieldAnnotationBinding == null) {
873 						if (debug.isDebugging()) {
874 							debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$
875 						}
876 
877 						continue;
878 					}
879 
880 					String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName();
881 					if (!REFERENCE_ANNOTATION.equals(annotationName)) {
882 						continue;
883 					}
884 
885 					HashMap<String, Object> annotationParams = null;
886 					// TODO do we really care about all fragments??
887 					for (Object fragmentElement : field.fragments()) {
888 						VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentElement;
889 						IVariableBinding fieldBinding = fragment.resolveBinding();
890 						if (fieldBinding == null) {
891 							if (debug.isDebugging()) {
892 								debug.trace(String.format("Unable to resolve binding for field: %s", fragment)); //$NON-NLS-1$
893 							}
894 
895 							continue;
896 						}
897 
898 						if (annotationParams == null) {
899 							annotationParams = new HashMap<>();
900 							for (IMemberValuePairBinding pair : fieldAnnotationBinding.getDeclaredMemberValuePairs()) {
901 								annotationParams.put(pair.getName(), pair.getValue());
902 							}
903 						}
904 
905 						String referenceName = (String) annotationParams.get("name"); //$NON-NLS-1$
906 						if (referenceName == null) {
907 							referenceName = fieldBinding.getName();
908 						}
909 
910 						IDSReference reference = refMap.remove(referenceName);
911 						if (reference == null) {
912 							reference = createReference(dsFactory);
913 						}
914 
915 						references.add(reference);
916 
917 						ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
918 						referenceProcessor.processReference(reference, field, fieldBinding, fieldAnnotation, fieldAnnotationBinding, annotationParams, referenceNames);
919 						requiredVersion = DSAnnotationVersion.V1_3;
920 					}
921 				}
922 			}
923 		}
924 
925 		String activate = null;
926 		boolean lookedForActivateMethod = false;
927 		IMethodBinding activateMethod = null;
928 		Annotation activateAnnotation = null;
929 		String deactivate = null;
930 		boolean lookedForDeactivateMethod = false;
931 		IMethodBinding deactivateMethod = null;
932 		Annotation deactivateAnnotation = null;
933 		String modified = null;
934 		IMethodBinding modifiedMethod = null;
935 		Annotation modifiedAnnotation = null;
936 
937 		for (MethodDeclaration method : type.getMethods()) {
938 			for (Object modifier : method.modifiers()) {
939 				if (!(modifier instanceof Annotation)) {
940 					continue;
941 				}
942 
943 				Annotation methodAnnotation = (Annotation) modifier;
944 				IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding();
945 				if (methodAnnotationBinding == null) {
946 					if (debug.isDebugging()) {
947 						debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$
948 					}
949 
950 					continue;
951 				}
952 
953 				String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName();
954 
955 				if (ACTIVATE_ANNOTATION.equals(annotationName)) {
956 					if (activate == null) {
957 						activate = method.getName().getIdentifier();
958 						if (specVersion == DSAnnotationVersion.V1_3) {
959 							activateMethod = method.resolveBinding();
960 						}
961 
962 						activateAnnotation = methodAnnotation;
963 						validateLifeCycleMethod(methodAnnotation, "activate", method); //$NON-NLS-1$
964 					} else if (!errorLevel.isIgnore()) {
965 						problemReporter.reportProblem(methodAnnotation, null, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier());
966 						if (activateAnnotation != null) {
967 							problemReporter.reportProblem(activateAnnotation, null, Messages.AnnotationProcessor_duplicateActivateMethod, activate);
968 							activateAnnotation = null;
969 						}
970 					}
971 
972 					continue;
973 				}
974 
975 				if (DEACTIVATE_ANNOTATION.equals(annotationName)) {
976 					if (deactivate == null) {
977 						deactivate = method.getName().getIdentifier();
978 						if (specVersion == DSAnnotationVersion.V1_3) {
979 							deactivateMethod = method.resolveBinding();
980 						}
981 
982 						deactivateAnnotation = methodAnnotation;
983 						validateLifeCycleMethod(methodAnnotation, "deactivate", method); //$NON-NLS-1$
984 					} else if (!errorLevel.isIgnore()) {
985 						problemReporter.reportProblem(methodAnnotation, null, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier());
986 						if (deactivateAnnotation != null) {
987 							problemReporter.reportProblem(deactivateAnnotation, null, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate);
988 							deactivateAnnotation = null;
989 						}
990 					}
991 
992 					continue;
993 				}
994 
995 				if (MODIFIED_ANNOTATION.equals(annotationName)) {
996 					if (modified == null) {
997 						modified = method.getName().getIdentifier();
998 						if (specVersion == DSAnnotationVersion.V1_3) {
999 							modifiedMethod = method.resolveBinding();
1000 						}
1001 
1002 						modifiedAnnotation = methodAnnotation;
1003 						validateLifeCycleMethod(methodAnnotation, "modified", method); //$NON-NLS-1$
1004 					} else if (!errorLevel.isIgnore()) {
1005 						problemReporter.reportProblem(methodAnnotation, null, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier());
1006 						if (modifiedAnnotation != null) {
1007 							problemReporter.reportProblem(modifiedAnnotation, null, Messages.AnnotationProcessor_duplicateModifiedMethod, modified);
1008 							modifiedAnnotation = null;
1009 						}
1010 					}
1011 
1012 					continue;
1013 				}
1014 
1015 				if (REFERENCE_ANNOTATION.equals(annotationName)) {
1016 					IMethodBinding methodBinding = method.resolveBinding();
1017 					if (methodBinding == null) {
1018 						if (debug.isDebugging()) {
1019 							debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$
1020 						}
1021 					} else {
1022 						HashMap<String, Object> annotationParams = new HashMap<>();
1023 						for (IMemberValuePairBinding pair : methodAnnotationBinding.getDeclaredMemberValuePairs()) {
1024 							annotationParams.put(pair.getName(), pair.getValue());
1025 						}
1026 
1027 						ReferenceProcessor referenceProcessor = new ReferenceProcessor(this, specVersion, requiredVersion, errorLevel, state.getMissingUnbindMethodLevel(), problemReporter);
1028 						String referenceName = referenceProcessor.getReferenceName(methodBinding.getName(), annotationParams);
1029 
1030 						IDSReference reference = refMap.remove(referenceName);
1031 						if (reference == null) {
1032 							reference = createReference(dsFactory);
1033 						}
1034 
1035 						references.add(reference);
1036 
1037 						requiredVersion = requiredVersion.max(referenceProcessor.processReference(reference, method, methodBinding, methodAnnotation, methodAnnotationBinding, annotationParams, referenceNames));
1038 					}
1039 
1040 					continue;
1041 				}
1042 			}
1043 		}
1044 
1045 		if (activate == null) {
1046 			// only remove activate="activate" if method not found
1047 			if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$
1048 					|| ((lookedForActivateMethod = true)
1049 							&& (activateMethod = findLifeCycleMethod(typeBinding, "activate")) == null)) { //$NON-NLS-1$
1050 				removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null);
1051 			}
1052 		} else {
1053 			component.setActivateMethod(activate);
1054 		}
1055 
1056 		if (deactivate == null) {
1057 			// only remove deactivate="deactivate" if method not found
1058 			if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$
1059 					|| ((lookedForDeactivateMethod = true)
1060 							&& (deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate")) == null)) { //$NON-NLS-1$
1061 				removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null);
1062 			}
1063 		} else {
1064 			component.setDeactivateMethod(deactivate);
1065 		}
1066 
1067 		if (modified == null) {
1068 			removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null);
1069 		} else {
1070 			component.setModifiedeMethod(modified);
1071 		}
1072 
1073 		LinkedHashMap<String, IDSProperty> newPropMap = new LinkedHashMap<>();
1074 
1075 		if (specVersion == DSAnnotationVersion.V1_3) {
1076 			// collect component property types from activate, modified, and deactivate methods
1077 			if (activateMethod == null && !lookedForActivateMethod) {
1078 				activateMethod = findLifeCycleMethod(typeBinding, "activate"); //$NON-NLS-1$
1079 			}
1080 
1081 			if (deactivateMethod == null && !lookedForDeactivateMethod) {
1082 				deactivateMethod = findLifeCycleMethod(typeBinding, "deactivate"); //$NON-NLS-1$
1083 			}
1084 
1085 			HashSet<ITypeBinding> cptClosure = new HashSet<>();
1086 
1087 			if (activateMethod != null) {
1088 				collectProperties(activateMethod, dsFactory, newPropMap, cptClosure);
1089 			}
1090 
1091 			if (modifiedMethod != null) {
1092 				collectProperties(modifiedMethod, dsFactory, newPropMap, cptClosure);
1093 			}
1094 
1095 			if (deactivateMethod != null) {
1096 				collectProperties(deactivateMethod, dsFactory, newPropMap, cptClosure);
1097 			}
1098 
1099 			if (!cptClosure.isEmpty()) {
1100 				requiredVersion = DSAnnotationVersion.V1_3;
1101 			}
1102 		}
1103 
1104 		IDSProperty[] propElements = component.getPropertyElements();
1105 		if (newPropMap.isEmpty() && properties.length == 0) {
1106 			removeChildren(component, Arrays.asList(propElements));
1107 		} else {
1108 			// build up new property elements
1109 			LinkedHashMap<String, IDSProperty> map = new LinkedHashMap<>(properties.length);
1110 			for (int i = 0; i < properties.length; ++i) {
1111 				String propertyStr = properties[i];
1112 				String[] pair = propertyStr.split("=", 2); //$NON-NLS-1$
1113 				int colon = pair[0].indexOf(':');
1114 				String propertyName, propertyType;
1115 				if (colon == -1) {
1116 					propertyName = pair[0];
1117 					propertyType = null;
1118 				} else {
1119 					propertyName = pair[0].substring(0, colon);
1120 					propertyType = pair[0].substring(colon + 1);
1121 				}
1122 
1123 				String propertyValue = pair.length > 1 ? pair[1].trim() : null;
1124 
1125 				IDSProperty property = map.get(propertyName);
1126 				if (property == null) {
1127 					// create a new property
1128 					property = dsFactory.createProperty();
1129 					map.put(propertyName, property);
1130 					property.setPropertyName(propertyName);
1131 					if (propertyType == null) {
1132 						removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_TYPE, null);	 // just remove the attribute completely so we can detect changes when reconciling
1133 					} else {
1134 						property.setPropertyType(propertyType);
1135 					}
1136 
1137 					property.setPropertyValue(propertyValue);
1138 					validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i);
1139 				} else {
1140 					// property is multi-valued
1141 					String content = property.getPropertyElemBody();
1142 					if (content == null) {
1143 						content = property.getPropertyValue();
1144 						property.setPropertyElemBody(content);
1145 						removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
1146 					}
1147 
1148 					if (!errorLevel.isIgnore()) {
1149 						String expected = property.getPropertyType() == null || property.getPropertyType().length() == 0 || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType()) ? Messages.AnnotationProcessor_stringOrEmpty : property.getPropertyType();
1150 						String actual = propertyType == null || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(propertyType) ? Messages.AnnotationProcessor_stringOrEmpty : propertyType;
1151 						if (!actual.equals(expected)) {
1152 							problemReporter.reportProblem(annotation, "property", i, NLS.bind(Messages.AnnotationProcessor_inconsistentComponentPropertyType, actual, expected), actual); //$NON-NLS-1$
1153 						} else {
1154 							validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i);
1155 						}
1156 					}
1157 
1158 					if (propertyValue != null) {
1159 						property.setPropertyElemBody(content + "\n" + pair[1]); //$NON-NLS-1$
1160 					}
1161 				}
1162 			}
1163 
1164 			// reconcile against existing property elements
1165 			HashMap<String, IDSProperty> propMap = new HashMap<>(propElements.length);
1166 			for (IDSProperty propElement : propElements) {
1167 				propMap.put(propElement.getPropertyName(), propElement);
1168 			}
1169 
1170 			newPropMap.keySet().removeAll(map.keySet()); // force re-insert (append)
1171 			newPropMap.putAll(map);
1172 
1173 			ArrayList<IDSProperty> propList = new ArrayList<>(newPropMap.values());
1174 			for (ListIterator<IDSProperty> i = propList.listIterator(); i.hasNext();) {
1175 				IDSProperty newProperty = i.next();
1176 				IDSProperty property = propMap.remove(newProperty.getPropertyName());
1177 				if (property == null) {
1178 					continue;
1179 				}
1180 
1181 				i.set(property);
1182 
1183 				String newPropertyType = newProperty.getPropertyType();
1184 				if (newPropertyType != null || !IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType())) {
1185 					property.setPropertyType(newPropertyType);
1186 				}
1187 
1188 				String newContent = newProperty.getPropertyElemBody();
1189 				if (newContent == null) {
1190 					property.setPropertyValue(newProperty.getPropertyValue());
1191 					IDocumentTextNode textNode = property.getTextNode();
1192 					if (textNode != null) {
1193 						property.removeTextNode();
1194 						if (property.isInTheModel() && property.isEditable()) {
1195 							model.fireModelChanged(new ModelChangedEvent(model, IModelChangedEvent.REMOVE, new Object[] { textNode }, null));
1196 						}
1197 					}
1198 				} else {
1199 					removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
1200 					String content = property.getPropertyElemBody();
1201 					if (content == null || !newContent.equals(normalizePropertyElemBody(content))) {
1202 						property.setPropertyElemBody(newContent);
1203 					}
1204 				}
1205 			}
1206 
1207 			int firstPos = propElements.length == 0
1208 					? 0	// insert first property element as first child of component
1209 							: component.indexOf(propElements[0]);
1210 
1211 			removeChildren(component, propMap.values());
1212 
1213 			addOrMoveChildren(component, propList, firstPos);
1214 		}
1215 
1216 		IDSProperties[] propFileElements = component.getPropertiesElements();
1217 		if (propertyFiles.length == 0) {
1218 			removeChildren(component, Arrays.asList(propFileElements));
1219 		} else {
1220 			HashMap<String, IDSProperties> propFileMap = new HashMap<>(propFileElements.length);
1221 			for (IDSProperties propFileElement : propFileElements) {
1222 				propFileMap.put(propFileElement.getEntry(), propFileElement);
1223 			}
1224 
1225 			ArrayList<IDSProperties> propFileList = new ArrayList<>(propertyFiles.length);
1226 			for (String propertyFile : propertyFiles) {
1227 				IDSProperties propertiesElement = propFileMap.remove(propertyFile);
1228 				if (propertiesElement == null) {
1229 					propertiesElement = dsFactory.createProperties();
1230 					propertiesElement.setInTheModel(false); // note: workaround for PDE bug
1231 					propertiesElement.setEntry(propertyFile);
1232 				}
1233 
1234 				propFileList.add(propertiesElement);
1235 			}
1236 
1237 			int firstPos;
1238 			if (propFileElements.length == 0) {
1239 				// insert first properties element after last property or (if none) first child of component
1240 				propElements = component.getPropertyElements();
1241 				firstPos = propElements.length == 0 ? 0 : component.indexOf(propElements[propElements.length - 1]) + 1;
1242 			} else {
1243 				firstPos = component.indexOf(propFileElements[0]);
1244 			}
1245 
1246 			removeChildren(component, propFileMap.values());
1247 
1248 			addOrMoveChildren(component, propFileList, firstPos);
1249 		}
1250 
1251 		if (references.isEmpty()) {
1252 			removeChildren(component, Arrays.asList(refElements));
1253 		} else {
1254 			// references must be declared in ascending lexicographical order of their names
1255 			Collections.sort(references, REF_NAME_COMPARATOR);
1256 
1257 			int firstPos;
1258 			if (refElements.length == 0) {
1259 				// insert first reference element after service element, or (if not present) last property or properties
1260 				service = component.getService();
1261 				if (service == null) {
1262 					firstPos = Math.max(0, indexOfLastPropertyOrProperties(component));
1263 				} else {
1264 					firstPos = component.indexOf(service) + 1;
1265 				}
1266 			} else {
1267 				firstPos = component.indexOf(refElements[0]);
1268 			}
1269 
1270 			removeChildren(component, refMap.values());
1271 
1272 			addOrMoveChildren(component, references, firstPos);
1273 		}
1274 
1275 		IDSImplementation impl = component.getImplementation();
1276 		if (impl == null) {
1277 			impl = dsFactory.createImplementation();
1278 			component.setImplementation(impl);
1279 		}
1280 
1281 		impl.setClassName(implClass);
1282 
1283 		String xmlns = requiredVersion.getNamespace();
1284 		if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$
1285 			xmlns = (String) value;
1286 			validateComponentXMLNS(annotation, xmlns, requiredVersion);
1287 		}
1288 
1289 		component.setNamespace(xmlns);
1290 	}
1291 
createReference(IDSDocumentFactory dsFactory)1292 	private IDSReference createReference(IDSDocumentFactory dsFactory) {
1293 		IDSReference reference = dsFactory.createReference();
1294 		// reset unnecessary defaults set by PDE
1295 		removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, null);
1296 		removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, null);
1297 		return reference;
1298 	}
1299 
removeChildren(IDSObject parent, Collection<? extends IDocumentElementNode> children)1300 	private void removeChildren(IDSObject parent, Collection<? extends IDocumentElementNode> children) {
1301 		for (IDocumentElementNode child : children) {
1302 			parent.removeChildNode(child, true);
1303 		}
1304 	}
1305 
removeAttribute(IDSObject obj, String name, String defaultValue)1306 	void removeAttribute(IDSObject obj, String name, String defaultValue) {
1307 		IDocumentAttributeNode attrNode = obj.getDocumentAttribute(name);
1308 		if (attrNode != null) {
1309 			// only remove if value is not default
1310 			String value = attrNode.getAttributeValue();
1311 			if (value != null && value.equals(defaultValue)) {
1312 				return;
1313 			}
1314 
1315 			obj.removeDocumentAttribute(attrNode);
1316 			if (obj.isInTheModel() && obj.isEditable()) {
1317 				obj.getModel().fireModelChanged(new ModelChangedEvent(obj.getModel(), ModelChangedEvent.REMOVE, new Object[] { attrNode }, null));
1318 			}
1319 		}
1320 	}
1321 
addOrMoveChildren(IDSObject parent, List<? extends IDSObject> children, int firstPos)1322 	private void addOrMoveChildren(IDSObject parent, List<? extends IDSObject> children, int firstPos) {
1323 		for (int i = 0, n = children.size(); i < n; ++i) {
1324 			IDSObject child = children.get(i);
1325 			if (child.isInTheModel()) {
1326 				int pos = parent.indexOf(child);
1327 				if (i == 0) {
1328 					if (firstPos < pos) {
1329 						// move to first place
1330 						moveChildNode(parent, child, firstPos - pos, true);
1331 					}
1332 				} else {
1333 					int prevPos = parent.indexOf(children.get(i - 1));
1334 					if (prevPos > pos) {
1335 						// move to previous sibling's position
1336 						moveChildNode(parent, child, prevPos - pos, true);
1337 					}
1338 				}
1339 			} else {
1340 				if (i == 0) {
1341 					if (firstPos == -1) {
1342 						parent.addChildNode(child, true);
1343 					} else {
1344 						// insert into first place
1345 						parent.addChildNode(child, firstPos, true);
1346 					}
1347 				} else {
1348 					// insert after preceding sibling
1349 					parent.addChildNode(child, parent.indexOf(children.get(i - 1)) + 1, true);
1350 				}
1351 			}
1352 		}
1353 	}
1354 
moveChildNode(IDocumentObject obj, IDocumentElementNode node, int newRelativeIndex, boolean fireEvent)1355 	private void moveChildNode(IDocumentObject obj, IDocumentElementNode node, int newRelativeIndex, boolean fireEvent) {
1356 		if (newRelativeIndex == 1 || newRelativeIndex == -1) {
1357 			obj.moveChildNode(node, newRelativeIndex, fireEvent);
1358 			return;
1359 		}
1360 
1361 		// workaround for PDE's busted DocumentObject.clone() method
1362 		int currentIndex = obj.indexOf(node);
1363 		if (currentIndex == -1) {
1364 			return;
1365 		}
1366 
1367 		int newIndex = newRelativeIndex + currentIndex;
1368 		if (newIndex < 0 || newIndex >= obj.getChildCount()) {
1369 			return;
1370 		}
1371 
1372 		obj.removeChildNode(node, fireEvent);
1373 		IDocumentElementNode clone = clone(obj, node);
1374 		obj.addChildNode(clone, newIndex, fireEvent);
1375 	}
1376 
clone(IDocumentObject obj, IDocumentElementNode node)1377 	private IDocumentElementNode clone(IDocumentObject obj, IDocumentElementNode node) {
1378 		// note: same exact impl as DocumentObject.clone()
1379 		// but here the deserialized object will actually resolve successfully
1380 		// because our classloader (with DSPropery visible) will be on top of the stack
1381 		// yay for Java serialization, *sigh*
1382 		IDocumentElementNode clone = null;
1383 		try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
1384 			// Serialize
1385 			try (ObjectOutputStream out = new ObjectOutputStream(bout)) {
1386 				out.writeObject(node);
1387 				out.flush();
1388 			}
1389 			byte[] bytes = bout.toByteArray();
1390 			// Deserialize
1391 			try (ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
1392 					ObjectInputStream in = new ObjectInputStream(bin)) {
1393 				clone = (IDocumentElementNode) in.readObject();
1394 			}
1395 			// Reconnect
1396 			clone.reconnect(obj, obj.getSharedModel());
1397 		} catch (IOException | ClassNotFoundException e) {
1398 			if (debug.isDebugging()) {
1399 				debug.trace("Error cloning element.", e); //$NON-NLS-1$
1400 			}
1401 		}
1402 
1403 		return clone;
1404 	}
1405 
indexOfLastPropertyOrProperties(IDSComponent component)1406 	private int indexOfLastPropertyOrProperties(IDSComponent component) {
1407 		int pos = -1;
1408 		IDSProperty[] propElements = component.getPropertyElements();
1409 		IDSProperties[] propFileElements = component.getPropertiesElements();
1410 		if (propElements.length > 0) {
1411 			pos = component.indexOf(propElements[propElements.length - 1]) + 1;
1412 		}
1413 
1414 		if (propFileElements.length > 0) {
1415 			int lastPos = component.indexOf(propFileElements[propFileElements.length - 1]) + 1;
1416 			if (lastPos > pos) {
1417 				pos = lastPos;
1418 			}
1419 		}
1420 
1421 		return pos;
1422 	}
1423 
normalizePropertyElemBody(String content)1424 	private String normalizePropertyElemBody(String content) {
1425 		StringBuilder buf = new StringBuilder(content.length());
1426 		try (BufferedReader reader = new BufferedReader(new StringReader(content))) {
1427 			String line;
1428 			while ((line = reader.readLine()) != null) {
1429 				String trimmed = line.trim();
1430 				if (trimmed.length() == 0) {
1431 					continue;
1432 				}
1433 
1434 				if (buf.length() > 0) {
1435 					buf.append('\n');
1436 				}
1437 
1438 				buf.append(trimmed);
1439 			}
1440 		} catch (IOException e) {
1441 			if (debug.isDebugging()) {
1442 				debug.trace("Error reading property element body.", e); //$NON-NLS-1$
1443 			}
1444 		}
1445 
1446 		return buf.toString();
1447 	}
1448 
collectProperties(IMethodBinding method, IDSDocumentFactory factory, Map<String, IDSProperty> properties, Collection<ITypeBinding> visited)1449 	private void collectProperties(IMethodBinding method, IDSDocumentFactory factory, Map<String, IDSProperty> properties, Collection<ITypeBinding> visited) {
1450 		for (ITypeBinding paramTypeBinding : method.getParameterTypes()) {
1451 			if (!paramTypeBinding.isAnnotation() || !visited.add(paramTypeBinding)) {
1452 				continue;
1453 			}
1454 
1455 			for (IMethodBinding methodBinding : paramTypeBinding.getDeclaredMethods()) {
1456 				if (!methodBinding.isAnnotationMember()) {
1457 					continue;
1458 				}
1459 
1460 				Object value = methodBinding.getDefaultValue();
1461 				if (value == null) {
1462 					continue;
1463 				}
1464 
1465 				ITypeBinding returnType = methodBinding.getReturnType();
1466 				if (returnType.isArray() ? returnType.getElementType().isAnnotation() : returnType.isAnnotation()) {
1467 					// TODO per spec we should report error, but we may have no annotation to report it on!
1468 					continue;
1469 				}
1470 
1471 				IDSProperty property = factory.createProperty();
1472 				property.setPropertyName(createPropertyName(methodBinding.getName()));
1473 				property.setPropertyType(getPropertyType(returnType));
1474 
1475 				if (returnType.isArray()) {
1476 					StringBuilder body = new StringBuilder();
1477 					for (Object item : ((Object[]) value)) {
1478 						String itemValue = getPropertyValue(item);
1479 						if (itemValue == null || (itemValue = itemValue.trim()).isEmpty()) {
1480 							continue;
1481 						}
1482 
1483 						if (body.length() > 0) {
1484 							body.append('\n');
1485 						}
1486 
1487 						body.append(itemValue);
1488 					}
1489 
1490 					removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null);
1491 					property.setPropertyElemBody(body.toString());
1492 				} else {
1493 					property.setPropertyValue(getPropertyValue(value));
1494 				}
1495 
1496 				properties.remove(property.getName()); // force re-insert (append)
1497 				properties.put(property.getName(), property);
1498 			}
1499 		}
1500 	}
1501 
createPropertyName(String name)1502 	private String createPropertyName(String name) {
1503 		StringBuilder buf = new StringBuilder(name.length());
1504 		char[] chars = name.toCharArray();
1505 		for (int i = 0, n = chars.length; i < n; ++i) {
1506 			if (chars[i] == '$') {
1507 				if (i == n - 1 || chars[i + 1] != '$') {
1508 					continue;
1509 				}
1510 
1511 				i++;
1512 			} else if (chars[i] == '_') {
1513 				if (i == n - 1 || chars[i + 1] != '_') {
1514 					chars[i] = '.';
1515 				} else {
1516 					i++;
1517 				}
1518 			}
1519 
1520 			buf.append(chars[i]);
1521 		}
1522 
1523 		return buf.toString();
1524 	}
1525 
getPropertyType(ITypeBinding type)1526 	private String getPropertyType(ITypeBinding type) {
1527 		if (type.isArray()) {
1528 			return getPropertyType(type.getElementType());
1529 		}
1530 
1531 		if (type.isPrimitive()) {
1532 			String name = type.getQualifiedName();
1533 			String result = PRIMITIVE_TYPE_MAP.get(name);
1534 			if (result != null) {
1535 				return result;
1536 			}
1537 		}
1538 
1539 		return IDSConstants.VALUE_PROPERTY_TYPE_STRING;
1540 	}
1541 
getPropertyValue(Object value)1542 	private String getPropertyValue(Object value) {
1543 		// enum
1544 		if (value instanceof IVariableBinding) {
1545 			return ((IVariableBinding) value).getName();
1546 		}
1547 
1548 		// class
1549 		if (value instanceof ITypeBinding) {
1550 			return ((ITypeBinding) value).getQualifiedName();
1551 		}
1552 
1553 		// everything else
1554 		return String.valueOf(value);
1555 	}
1556 
validateComponentName(Annotation annotation, String name)1557 	private void validateComponentName(Annotation annotation, String name) {
1558 		if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(name).matches()) {
1559 			problemReporter.reportProblem(annotation, "name", NLS.bind(Messages.AnnotationProcessor_invalidComponentName, name), name); //$NON-NLS-1$
1560 		}
1561 	}
1562 
validateComponentService(Annotation annotation, ITypeBinding componentType, ITypeBinding serviceType, int index)1563 	private void validateComponentService(Annotation annotation, ITypeBinding componentType, ITypeBinding serviceType, int index) {
1564 		if (!errorLevel.isIgnore() && !componentType.isAssignmentCompatible(serviceType)) {
1565 			problemReporter.reportProblem(annotation, "service", NLS.bind(Messages.AnnotationProcessor_invalidComponentService, serviceType.getName()), serviceType.getName()); //$NON-NLS-1$
1566 		}
1567 	}
1568 
validateComponentFactory(Annotation annotation, String factory)1569 	private void validateComponentFactory(Annotation annotation, String factory) {
1570 		if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(factory).matches()) {
1571 			problemReporter.reportProblem(annotation, "factory", NLS.bind(Messages.AnnotationProcessor_invalidComponentFactoryName, factory), factory); //$NON-NLS-1$
1572 		}
1573 	}
1574 
validateComponentProperty(Annotation annotation, String name, String type, String value, int index)1575 	private void validateComponentProperty(Annotation annotation, String name, String type, String value, int index) {
1576 		if (errorLevel.isIgnore()) {
1577 			return;
1578 		}
1579 
1580 		if (PROPERTY_TYPES.contains(type)) {
1581 			if (name == null || name.trim().length() == 0) {
1582 				problemReporter.reportProblem(annotation, "property", index, Messages.AnnotationProcessor_invalidComponentProperty_nameRequired, name); //$NON-NLS-1$
1583 			}
1584 
1585 			if (value == null) {
1586 				problemReporter.reportProblem(annotation, "property", index, Messages.AnnotationProcessor_invalidComponentProperty_valueRequired, name); //$NON-NLS-1$
1587 			} else {
1588 				try {
1589 					if (IDSConstants.VALUE_PROPERTY_TYPE_LONG.equals(type)) {
1590 						Long.valueOf(value);
1591 					} else if (IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE.equals(type)) {
1592 						Double.valueOf(value);
1593 					} else if (IDSConstants.VALUE_PROPERTY_TYPE_FLOAT.equals(type)) {
1594 						Float.valueOf(value);
1595 					} else if (IDSConstants.VALUE_PROPERTY_TYPE_INTEGER.equals(type) || IDSConstants.VALUE_PROPERTY_TYPE_CHAR.equals(type)) {
1596 						Integer.valueOf(value);
1597 					} else if (IDSConstants.VALUE_PROPERTY_TYPE_BYTE.equals(type)) {
1598 						Byte.valueOf(value);
1599 					} else if (IDSConstants.VALUE_PROPERTY_TYPE_SHORT.equals(type)) {
1600 						Short.valueOf(value);
1601 					}
1602 				} catch (NumberFormatException e) {
1603 					problemReporter.reportProblem(annotation, "property", index, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyValue, type, value), String.valueOf(value)); //$NON-NLS-1$
1604 				}
1605 			}
1606 		} else {
1607 			problemReporter.reportProblem(annotation, "property", index, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyType, type), String.valueOf(type)); //$NON-NLS-1$
1608 		}
1609 	}
1610 
validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files)1611 	private void validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files) {
1612 		if (errorLevel.isIgnore()) {
1613 			return;
1614 		}
1615 
1616 		for (int i = 0; i < files.length; ++i) {
1617 			String file = files[i];
1618 			IFile wsFile = PDEProject.getBundleRelativeFile(project, new Path(file));
1619 			if (!wsFile.exists()) {
1620 				problemReporter.reportProblem(annotation, "properties", i, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file), file); //$NON-NLS-1$
1621 			}
1622 		}
1623 	}
1624 
validateComponentXMLNS(Annotation annotation, String xmlns, DSAnnotationVersion requiredVersion)1625 	private void validateComponentXMLNS(Annotation annotation, String xmlns, DSAnnotationVersion requiredVersion) {
1626 		if (errorLevel.isIgnore()) {
1627 			return;
1628 		}
1629 
1630 		DSAnnotationVersion specifiedVersion = DSAnnotationVersion.fromNamespace(xmlns);
1631 		if (specifiedVersion == null || requiredVersion.compareTo(specifiedVersion) > 0) {
1632 			problemReporter.reportProblem(annotation, "xmlns", NLS.bind(Messages.AnnotationProcessor_invalidComponentDescriptorNamespace, xmlns), xmlns); //$NON-NLS-1$
1633 		}
1634 	}
1635 
validateComponentConfigPID(Annotation annotation, String configPid, int index)1636 	private void validateComponentConfigPID(Annotation annotation, String configPid, int index) {
1637 		if (!errorLevel.isIgnore() && !PID_PATTERN.matcher(configPid).matches()) {
1638 			problemReporter.reportProblem(annotation, "configurationPid", index, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$
1639 		}
1640 	}
1641 
validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method)1642 	private void validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method) {
1643 		if (errorLevel.isIgnore()) {
1644 			return;
1645 		}
1646 
1647 		IMethodBinding methodBinding = method.resolveBinding();
1648 		if (methodBinding == null) {
1649 			if (debug.isDebugging()) {
1650 				debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$
1651 			}
1652 
1653 			return;
1654 		}
1655 
1656 		if (Modifier.isStatic(methodBinding.getModifiers())) {
1657 			problemReporter.reportProblem(annotation, methodName, Messages.AnnotationProcessor_invalidLifecycleMethod_static);
1658 		}
1659 
1660 		String returnTypeName = methodBinding.getReturnType().getName();
1661 		if (!Void.TYPE.getName().equals(returnTypeName)) {
1662 			problemReporter.reportProblem(annotation, methodName, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodReturnType, methodName, returnTypeName), returnTypeName);
1663 		}
1664 
1665 		ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes();
1666 
1667 		if (paramTypeBindings.length == 0) {
1668 			// no-arg method
1669 			return;
1670 		}
1671 
1672 		// every argument must be either Map, Annotation (component property type), ComponentContext, or BundleContext
1673 		boolean hasMap = false;
1674 		boolean hasCompCtx = false;
1675 		boolean hasBundleCtx = false;
1676 		boolean hasInt = false;
1677 		HashSet<ITypeBinding> annotationParams = new HashSet<>(1);
1678 
1679 		for (ITypeBinding paramTypeBinding : paramTypeBindings) {
1680 			ITypeBinding paramTypeErasure = paramTypeBinding.getErasure();
1681 			String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
1682 			boolean isDuplicate = false;
1683 
1684 			if (paramTypeBinding.isAnnotation() && specVersion == DSAnnotationVersion.V1_3) {
1685 				if (!annotationParams.add(paramTypeBinding)) {
1686 					isDuplicate = true;
1687 				}
1688 			} else if (Map.class.getName().equals(paramTypeName)) {
1689 				if (hasMap) {
1690 					isDuplicate = true;
1691 				} else {
1692 					hasMap = true;
1693 				}
1694 			} else if (COMPONENT_CONTEXT.equals(paramTypeName)) {
1695 				if (hasCompCtx) {
1696 					isDuplicate = true;
1697 				} else {
1698 					hasCompCtx = true;
1699 				}
1700 			} else if (BundleContext.class.getName().equals(paramTypeName)) {
1701 				if (hasBundleCtx) {
1702 					isDuplicate = true;
1703 				} else {
1704 					hasBundleCtx = true;
1705 				}
1706 			} else if ("deactivate".equals(methodName) //$NON-NLS-1$
1707 					&& (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) {
1708 				if (hasInt) {
1709 					isDuplicate = true;
1710 				} else {
1711 					hasInt = true;
1712 				}
1713 			} else {
1714 				problemReporter.reportProblem(annotation, methodName, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName);
1715 			}
1716 
1717 			if (isDuplicate) {
1718 				problemReporter.reportProblem(annotation, methodName, NLS.bind(Messages.AnnotationProcessor_duplicateLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName);
1719 			}
1720 		}
1721 	}
1722 
findLifeCycleMethod(ITypeBinding componentClass, String methodName)1723 	private IMethodBinding findLifeCycleMethod(ITypeBinding componentClass, String methodName) {
1724 		for (IMethodBinding methodBinding : componentClass.getDeclaredMethods()) {
1725 			if (methodName.equals(methodBinding.getName())
1726 					&& Void.TYPE.getName().equals(methodBinding.getReturnType().getName())) {
1727 				ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes();
1728 
1729 				// every argument must be either Map, Annotation (component property type), ComponentContext, or BundleContext
1730 				boolean hasMap = false;
1731 				boolean hasCompCtx = false;
1732 				boolean hasBundleCtx = false;
1733 				boolean hasInt = false;
1734 				boolean isInvalid = false;
1735 				HashSet<ITypeBinding> annotationParams = new HashSet<>(1);
1736 				for (ITypeBinding paramTypeBinding : paramTypeBindings) {
1737 					if (paramTypeBinding.isAnnotation()) {
1738 						if (specVersion == DSAnnotationVersion.V1_3 && annotationParams.add(paramTypeBinding)) {
1739 							// component property type (multiple arguments allowed)
1740 							continue;
1741 						}
1742 
1743 						isInvalid = true;
1744 						break;
1745 					}
1746 
1747 					ITypeBinding paramTypeErasure = paramTypeBinding.getErasure();
1748 					String paramTypeName = paramTypeErasure.isMember() ? paramTypeErasure.getBinaryName() : paramTypeErasure.getQualifiedName();
1749 
1750 					if (Map.class.getName().equals(paramTypeName)) {
1751 						if (hasMap) {
1752 							isInvalid = true;
1753 						} else {
1754 							hasMap = true;
1755 						}
1756 					} else if (COMPONENT_CONTEXT.equals(paramTypeName)) {
1757 						if (hasCompCtx) {
1758 							isInvalid = true;
1759 						} else {
1760 							hasCompCtx = true;
1761 						}
1762 					} else if (BundleContext.class.getName().equals(paramTypeName)) {
1763 						if (hasBundleCtx) {
1764 							isInvalid = true;
1765 						} else {
1766 							hasBundleCtx = true;
1767 						}
1768 					} else if ("deactivate".equals(methodName) //$NON-NLS-1$
1769 							&& (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) {
1770 						if (hasInt) {
1771 							isInvalid = true;
1772 						} else {
1773 							hasInt = true;
1774 						}
1775 					} else {
1776 						isInvalid = true;
1777 					}
1778 
1779 					if (isInvalid) {
1780 						break;
1781 					}
1782 				}
1783 
1784 				if (!isInvalid) {
1785 					return methodBinding;
1786 				}
1787 			}
1788 		}
1789 
1790 		return null;
1791 	}
1792 }