/******************************************************************************* * Copyright (c) 2007 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.seam.internal.core.scanner.lib; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; import org.eclipse.jdt.internal.compiler.env.IBinaryField; import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.jboss.tools.common.model.project.ext.IValueInfo; import org.jboss.tools.common.model.project.ext.impl.ValueInfo; import org.jboss.tools.seam.core.BeanType; import org.jboss.tools.seam.core.BijectedAttributeType; import org.jboss.tools.seam.core.SeamComponentMethodType; import org.jboss.tools.seam.core.SeamCorePlugin; import org.jboss.tools.seam.internal.core.AbstractContextVariable; import org.jboss.tools.seam.internal.core.BijectedAttribute; import org.jboss.tools.seam.internal.core.DataModelSelectionAttribute; import org.jboss.tools.seam.internal.core.SeamAnnotatedFactory; import org.jboss.tools.seam.internal.core.SeamComponentMethod; import org.jboss.tools.seam.internal.core.SeamJavaComponentDeclaration; import org.jboss.tools.seam.internal.core.SeamMessages; import org.jboss.tools.seam.internal.core.scanner.LoadedDeclarations; import org.jboss.tools.seam.internal.core.scanner.Util; import org.jboss.tools.seam.internal.core.scanner.java.SeamAnnotations; public class TypeScanner implements SeamAnnotations { /** * Checks if class may be a source of seam components. * @param f * @return */ public boolean isLikelyComponentSource(ClassFileReader cls) { return cls != null && isSeamAnnotatedClass(cls); } /** * Loads seam components from class. * Returns object that contains loaded components or null; * @param type * @param cls * @param path * @return */ public LoadedDeclarations parse(IType type, ClassFileReader cls, IPath path) { if(!isLikelyComponentSource(cls)) return null; LoadedDeclarations ds = new LoadedDeclarations(); SeamJavaComponentDeclaration component = new SeamJavaComponentDeclaration(); component.setSourcePath(path); component.setId(type); component.setType(type); component.setClassName(type.getFullyQualifiedName()); process(cls, component, ds); ds.getComponents().add(component); for (int i = 0; i < ds.getFactories().size(); i++) { AbstractContextVariable f = (AbstractContextVariable)ds.getFactories().get(i); f.setSourcePath(path); f.getId(); } return ds; } /** * Check if class has at least one seam annotation. * @param cls * @return */ boolean isSeamAnnotatedClass(ClassFileReader cls) { if(cls == null || ((cls.getModifiers() & ClassFileConstants.AccInterface) > 0)) return false; IBinaryAnnotation[] as = cls.getAnnotations(); if(as != null) for (int i = 0; i < as.length; i++) { String type = getTypeName(as[i]); if(Util.isSeamAnnotationType(type)) { return true; } } return false; } public static String getTypeName(IBinaryAnnotation a) { if(a.getTypeName() == null) return ""; //$NON-NLS-1$ String t = new String(a.getTypeName()); if(t.startsWith("L") && t.endsWith(";")) { //$NON-NLS-1$ //$NON-NLS-2$ t = t.substring(1, t.length() - 1); } t = t.replace('/', '.'); return t; } Map<String,IBinaryAnnotation> getSeamAnnotations(IBinaryAnnotation[] as) { if(as == null || as.length == 0) return null; Map<String,IBinaryAnnotation> map = null; for (int i = 0; i < as.length; i++) { String type = getTypeName(as[i]); if(Util.isSeamAnnotationType(type)) { if(map == null) map = new HashMap<String, IBinaryAnnotation>(); map.put(type, as[i]); } } return map; } private void process(ClassFileReader cls, SeamJavaComponentDeclaration component, LoadedDeclarations ds) { Map<String, IBinaryAnnotation> map = getSeamAnnotations(cls.getAnnotations()); if(map != null) { IBinaryAnnotation a = map.get(NAME_ANNOTATION_TYPE); if(a != null) { String name = (String)getValue(a, "value"); //$NON-NLS-1$ if(name != null) component.setName(name); } a = map.get(SCOPE_ANNOTATION_TYPE); if(a != null) { Object scope = getValue(a, "value"); //$NON-NLS-1$ if(scope != null) component.setScope(scope.toString()); } a = map.get(INSTALL_ANNOTATION_TYPE); if(a != null) { String precedence = getValue(a, "precedence"); //$NON-NLS-1$ try { int i = Integer.parseInt(precedence); component.setPrecedence(i); } catch (NumberFormatException e) { //ignore } } } Map<BeanType, IValueInfo> types = new HashMap<BeanType, IValueInfo>(); for (int i = 0; i < BeanType.values().length; i++) { BeanType t = BeanType.values()[i]; IBinaryAnnotation a = map.get(t.getAnnotationType()); if(a != null) { ValueInfo v = new ValueInfo(); v.setValue("true"); //$NON-NLS-1$ types.put(t, v); } } if(!types.isEmpty()) { component.setTypes(types); } IBinaryMethod[] ms = null; try { ms = cls.getMethods(); } catch (NoClassDefFoundError e) { //ignore } if(ms != null) for (int i = 0; i < ms.length; i++) { process(ms[i], component, ds); } // IBinaryField[] fs = null; // try { // fs = cls.getFields(); // } catch (NoClassDefFoundError e) { // //ignore // } // if(fs != null) for (int i = 0; i < fs.length; i++) { // //TODO // } } private void process(IBinaryMethod m, SeamJavaComponentDeclaration component, LoadedDeclarations ds) { Map<String,IBinaryAnnotation> map = getSeamAnnotations(m.getAnnotations()); if(map == null || map.isEmpty()) return; IBinaryAnnotation a = map.get(FACTORY_ANNOTATION_TYPE); if(a != null) { processFactory(m, a, component, ds); } processBijection(m, map, component, ds); processComponentMethod(m, map, component, ds); } private void processFactory(IBinaryMethod m, IBinaryAnnotation a, SeamJavaComponentDeclaration component, LoadedDeclarations ds) { if(a == null) return; String name = (String)getValue(a, "value"); //$NON-NLS-1$ if(name == null || name.length() == 0) { name = new String(m.getSelector()); } SeamAnnotatedFactory factory = ("org.jboss.seam.international.messages".equals(name) && new String(m.getSelector()).equals("getMessages")) ? new SeamMessages() : new SeamAnnotatedFactory(); factory.setParentDeclaration(component); factory.setSourcePath(component.getSourcePath()); ds.getFactories().add(factory); IMethod im = findIMethod(component, m); factory.setId(im); factory.setSourceMember(im); factory.setName(name); Object scope = getValue(a, "scope"); //$NON-NLS-1$ if(scope != null) factory.setScopeAsString(scope.toString()); Object autoCreate = getValue(a, "autoCreate"); //$NON-NLS-1$ if(autoCreate instanceof Boolean) { factory.setAutoCreate((Boolean)autoCreate); } } private void processBijection(IBinaryMethod m, Map<String,IBinaryAnnotation> map, SeamJavaComponentDeclaration component, LoadedDeclarations ds) { Map<BijectedAttributeType, IBinaryAnnotation> as = new HashMap<BijectedAttributeType, IBinaryAnnotation>(); List<BijectedAttributeType> types = new ArrayList<BijectedAttributeType>(); IBinaryAnnotation main = null; for (int i = 0; i < BijectedAttributeType.values().length; i++) { IBinaryAnnotation a = map.get(BijectedAttributeType.values()[i].getAnnotationType()); if(a != null) { as.put(BijectedAttributeType.values()[i], a); if(main == null) main = a; types.add(BijectedAttributeType.values()[i]); } } if(as.isEmpty()) return; boolean isDataModelSelectionType = !types.get(0).isUsingMemberName(); BijectedAttribute att = (!isDataModelSelectionType) ? new BijectedAttribute() : new DataModelSelectionAttribute(); component.addBijectedAttribute(att); att.setTypes(types.toArray(new BijectedAttributeType[0])); String name = (String)getValue(main, "value"); //$NON-NLS-1$ att.setValue(name); if(name == null || name.length() == 0 || isDataModelSelectionType) { name = new String(m.getSelector()); } att.setName(name); Object scope = getValue(main, "scope"); //$NON-NLS-1$ if(scope != null) att.setScopeAsString(scope.toString()); IMember im = findIMethod(component, m); att.setSourceMember(im); } void processComponentMethod(IBinaryMethod m, Map<String,IBinaryAnnotation> map, SeamJavaComponentDeclaration component, LoadedDeclarations ds) { SeamComponentMethod cm = null; for (int i = 0; i < SeamComponentMethodType.values().length; i++) { SeamComponentMethodType type = SeamComponentMethodType.values()[i]; IBinaryAnnotation a = map.get(type.getAnnotationType()); if(a == null) continue; if(cm == null) { cm = new SeamComponentMethod(); component.addMethod(cm); IMethod im = findIMethod(component, m); cm.setSourceMember(im); cm.setId(im); } cm.getTypes().add(type); } } public static String getValue(IBinaryAnnotation a, String method) { try { IBinaryElementValuePair[] ps = a.getElementValuePairs(); if(ps != null) for (int i = 0; i < ps.length; i++) { if(method.equals(new String(ps[i].getName()))) { Object v = ps[i].getValue(); if(v == null) return null; if(v instanceof EnumConstantSignature) { EnumConstantSignature cs = (EnumConstantSignature)v; char[] cv = cs.getEnumConstantName(); return cv == null ? null : new String(cv); } else if(v instanceof Constant) { Constant ic = (Constant)v; return ic.stringValue(); } v = v.toString(); return (String)v; } } } catch (Throwable e) { SeamCorePlugin.getPluginLog().logError(e); } return null; } private IMethod findIMethod(SeamJavaComponentDeclaration component, IBinaryMethod m) { String name = new String(m.getSelector()); IType type = (IType)component.getSourceMember(); String signature = new String(m.getMethodDescriptor()); IMethod im = null; IMethod[] ms = null; try { ms = type.getMethods(); } catch (JavaModelException e) { SeamCorePlugin.getDefault().logError(e); } if(ms != null) for (int i = 0; i < ms.length; i++) { if(!ms[i].getElementName().equals(name)) continue; //check parameters try { if(ms[i].getParameterNames().length != m.getArgumentNames().length) continue; } catch (JavaModelException e) { continue; } //compare return ms[i]; } return null; } private IField findIField(SeamJavaComponentDeclaration component, Field m) { IType type = (IType)component.getSourceMember(); return type.getField(m.getName()); } }