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 }