/*- * Copyright (C) 2011-2014 by Iwao AVE! * This program is made available under the terms of the MIT License. */ package org.eclipselabs.stlipse.apt; import static org.eclipselabs.stlipse.util.StripesClasses.*; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IJavaProject; import org.eclipselabs.stlipse.Activator; import org.eclipselabs.stlipse.cache.BeanPropertyCache; import org.eclipselabs.stlipse.cache.BeanPropertyVisitor; import org.eclipselabs.stlipse.cache.EventProperty; import com.sun.mirror.apt.Messager; import com.sun.mirror.declaration.AnnotationMirror; import com.sun.mirror.declaration.AnnotationTypeElementDeclaration; import com.sun.mirror.declaration.AnnotationValue; import com.sun.mirror.declaration.ClassDeclaration; import com.sun.mirror.declaration.Declaration; import com.sun.mirror.declaration.FieldDeclaration; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.util.SimpleDeclarationVisitor; /** * @author Iwao AVE! */ public class StlipseAnnotationVisitor extends SimpleDeclarationVisitor { private String annotationType; private IJavaProject project; private String qualifiedName; private Messager messager; private boolean hasEventOption; public StlipseAnnotationVisitor( String annotationType, IJavaProject project, String qualifiedName, Messager messager) { super(); this.annotationType = annotationType; this.project = project; this.qualifiedName = qualifiedName; this.messager = messager; this.hasEventOption = AFTER.equals(annotationType) || BEFORE.equals(annotationType) || WIZARD.equals(annotationType) || VALIDATE.equals(annotationType) || VALIDATION_METHOD.equals(annotationType); } @Override public void visitClassDeclaration(ClassDeclaration d) { if (STRICT_BINDING.equals(annotationType)) { validateStrictBinding(d); } if (hasEventOption) { validateEventOption(d, eventOptionName(annotationType)); } } @Override public void visitFieldDeclaration(FieldDeclaration d) { if (VALIDATE_NESTED_PROPERTIES.equals(annotationType)) { String propertyName = d.getSimpleName(); validateValidateOptions(d, propertyName); } if (hasEventOption) { validateEventOption(d, eventOptionName(annotationType)); } } @Override public void visitMethodDeclaration(MethodDeclaration d) { if (VALIDATE_NESTED_PROPERTIES.equals(annotationType)) { String propertyName = BeanPropertyVisitor.getFieldNameFromAccessor(d.getSimpleName()); validateValidateOptions(d, propertyName); } if (hasEventOption) { validateEventOption(d, eventOptionName(annotationType)); } } private void validateEventOption(Declaration d, final String eventOptionName) { Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors(); for (AnnotationMirror mirror : mirrors) { try { Map<AnnotationTypeElementDeclaration, AnnotationValue> valueMap = mirror.getElementValues(); Set<Entry<AnnotationTypeElementDeclaration, AnnotationValue>> valueSet = valueMap.entrySet(); for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> valueEntry : valueSet) { String optionName = valueEntry.getKey().getSimpleName(); if (eventOptionName.equals(optionName)) { @SuppressWarnings("unchecked") List<AnnotationValue> properties = (List<AnnotationValue>)valueEntry.getValue() .getValue(); validateEventName(properties); } } } catch (IllegalStateException e) { // This seems to happen when the annotation value is missing or incomplete. // Might be an Eclipse bug. Activator.log(Status.INFO, "Exception thrown while processing @StrictBinding", e); } } } private void validateEventName(List<AnnotationValue> properties) { Map<String, EventProperty> eventHandlers = BeanPropertyCache.getEventHandlers(project, qualifiedName); for (AnnotationValue property : properties) { String event = property.getValue().toString(); if (event.startsWith("!") && event.length() > 2) event = event.substring(1); if (!eventHandlers.containsKey(event)) { messager.printError(property.getPosition(), "Event handler not found: " + event); } } } private void validateStrictBinding(ClassDeclaration d) { Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors(); for (AnnotationMirror mirror : mirrors) { try { Map<AnnotationTypeElementDeclaration, AnnotationValue> valueMap = mirror.getElementValues(); Set<Entry<AnnotationTypeElementDeclaration, AnnotationValue>> valueSet = valueMap.entrySet(); for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> valueEntry : valueSet) { String optionName = valueEntry.getKey().getSimpleName(); if ("allow".equals(optionName) || "deny".equals(optionName)) { @SuppressWarnings("unchecked") List<AnnotationValue> properties = (List<AnnotationValue>)valueEntry.getValue() .getValue(); for (AnnotationValue property : properties) { Map<String, String> matched = BeanPropertyCache.searchFields(project, qualifiedName, property.getValue().toString(), false, -1, true); if (matched.size() == 0) { messager.printError(property.getPosition(), "No writable property found: " + property.getValue().toString()); } } } } } catch (IllegalStateException e) { // This seems to happen when the annotation value is missing or incomplete. // Might be an Eclipse bug. Activator.log(Status.INFO, "Exception thrown while processing @StrictBinding", e); } } } private void validateValidateOptions(Declaration d, String propertyName) { Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors(); for (AnnotationMirror mirror1 : mirrors) { Map<AnnotationTypeElementDeclaration, AnnotationValue> valueMap = mirror1.getElementValues(); Set<Entry<AnnotationTypeElementDeclaration, AnnotationValue>> valueSet = valueMap.entrySet(); for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> nestedOpts : valueSet) { // Though @ValidateNestedProperties has only one property // that is a list of @Validate, this returns String // when the annotation value is missing or incomplete. Object values = nestedOpts.getValue().getValue(); if (values instanceof List<?>) { @SuppressWarnings("unchecked") List<AnnotationValue> validateAnnotations = (List<AnnotationValue>)values; for (AnnotationValue validate : validateAnnotations) { AnnotationMirror mirror2 = (AnnotationMirror)validate.getValue(); Map<AnnotationTypeElementDeclaration, AnnotationValue> validateOpts = mirror2.getElementValues(); for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> validateOpt : validateOpts.entrySet()) { String optionName = validateOpt.getKey().getSimpleName(); if ("field".equals(optionName)) { StringBuilder searchStr = new StringBuilder(propertyName); AnnotationValue annotationValue = validateOpt.getValue(); searchStr.append('.').append(annotationValue); Map<String, String> matched = BeanPropertyCache.searchFields(project, qualifiedName, searchStr.toString(), false, -1, true); if (matched.size() == 0) { messager.printError(annotationValue.getPosition(), "No writable property found: " + searchStr.toString()); } } else if ("on".equals(optionName)) { @SuppressWarnings("unchecked") List<AnnotationValue> properties = (List<AnnotationValue>)validateOpt.getValue() .getValue(); validateEventName(properties); } } } } } } } private static String eventOptionName(String annotation) { return WIZARD.equals(annotation) ? "startEvents" : "on"; } }