/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.javaparser; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.foundation.FlexoException; import org.openflexo.foundation.dm.DMEntity; import org.openflexo.foundation.dm.DMModel; import org.openflexo.foundation.dm.DMPackage; import org.openflexo.foundation.dm.DMSet.PackageReference.ClassReference; import org.openflexo.foundation.dm.DMType; import org.openflexo.foundation.dm.LoadableDMEntity; import org.openflexo.javaparser.FJPJavaSource.FJPImportDeclarations.FJPImportDeclaration; import com.thoughtworks.qdox.model.Type; /** * Utility class used to resolve types in the context of Flexo Data Model (mapping between Type and DMEntity) * * @author sylvain * */ public class FJPTypeResolver { private static final Logger logger = Logger.getLogger(FJPTypeResolver.class.getPackage().getName()); private static DMEntity entityForType(Type type, DMModel dataModel) { DMEntity returned = dataModel.getCachedEntitiesForTypes().get(type.getValue()); return returned; } private static void storeEntityForType(DMEntity entity, Type type, DMModel dataModel) { dataModel.getCachedEntitiesForTypes().put(type.getValue(), entity); } private static enum ClassResolvingMethod { Cache, DataModel, ImportDeclarations, Context, ClassPath } /*public static long timeSpendResolvingTypes = 0; public static Vector<Type> searchedTypes;*/ /*public static void startDebug() { timeSpendResolvingTypes = 0; searchedTypes = new Vector<Type>(); } public static void stopDebug() { logger.info("Searched "+searchedTypes.size()+" types"); Date date1 = new Date(); for (Type t : searchedTypes) { t.getValue(); } Date date2 = new Date(); logger.info("Time getValue() "+(date2.getTime()-date1.getTime())+" ms"); Date date3 = new Date(); for (Type t : searchedTypes) { t.toString(); } Date date4 = new Date(); logger.info("Time toString() "+(date4.getTime()-date3.getTime())+" ms"); Date date5 = new Date(); for (Type t : searchedTypes) { t.hashCode(); } Date date6 = new Date(); logger.info("Time hashCode() "+(date6.getTime()-date5.getTime())+" ms"); for (Type t : searchedTypes) { //logger.info("Type "+t.hashCode()+" library "+t.getJavaClassParent().getClassLibrary()); } }*/ protected static class TypeLookupInfo { DMEntity foundEntity = null; boolean resolvedButEntityNotAvailableYet = false; } private static TypeLookupInfo lookupEntity(Type type, DMModel dataModel, FJPDMSet context, FJPJavaSource source, boolean resolveNow) throws CrossReferencedEntitiesException { // Date date1 = new Date(); TypeLookupInfo returned = new TypeLookupInfo(); returned.foundEntity = null; returned.resolvedButEntityNotAvailableYet = false; ClassResolvingMethod m = ClassResolvingMethod.Cache; while (returned.foundEntity == null && m != null && !returned.resolvedButEntityNotAvailableYet) { if (m == ClassResolvingMethod.Cache) { returned.foundEntity = entityForType(type, dataModel); } else if (m == ClassResolvingMethod.DataModel) { returned.foundEntity = dataModel.getDMEntity(type.getValue()); if (returned.foundEntity == null && source.getPackage() == null) { returned.foundEntity = dataModel.getDMEntity(DMPackage.DEFAULT_PACKAGE_NAME, type.getValue()); } } else if (m == ClassResolvingMethod.ImportDeclarations) { if (source != null) { for (FJPImportDeclaration importDeclaration : source.getImportDeclarations().getImportDeclarations()) { if (importDeclaration.getImportDeclaration().endsWith(".*")) { String importPrefix = importDeclaration.getImportDeclaration().substring(0, importDeclaration.getImportDeclaration().lastIndexOf(".*")); returned.foundEntity = dataModel.getDMEntity(importPrefix + "." + type.getValue()); if (returned.foundEntity != null) { source.getQDSource().getClassLibrary().add(importPrefix + "." + type.getValue()); break; } } else if (importDeclaration.getImportDeclaration().endsWith(type.getValue())) { returned.foundEntity = dataModel.getDMEntity(importDeclaration.getImportDeclaration()); if (returned.foundEntity != null) { source.getQDSource().getClassLibrary().add(importDeclaration.getImportDeclaration()); break; } } } } } else if (m == ClassResolvingMethod.Context) { if (context != null) { ClassReference classRef = context.getClassReference(type.getValue()); if (classRef != null) { if (resolveNow) { returned.foundEntity = context.justResolvedEntity(classRef); if (returned.foundEntity == null) { throw new CrossReferencedEntitiesException(); } } else { returned.resolvedButEntityNotAvailableYet = true; } } } } else if (m == ClassResolvingMethod.ClassPath) { try { Class<?> searchedClass = Class.forName(type.getValue()); if (resolveNow) { returned.foundEntity = LoadableDMEntity.createLoadableDMEntity(dataModel.getJDKRepository(), searchedClass, false, false); } else { returned.resolvedButEntityNotAvailableYet = true; } } catch (ClassNotFoundException e) { } } if (returned.foundEntity == null) { if (m.ordinal() + 1 < ClassResolvingMethod.values().length) { m = ClassResolvingMethod.values()[m.ordinal() + 1]; } else { m = null; } } } if (returned.foundEntity != null) { storeEntityForType(returned.foundEntity, type, dataModel); } /*Date date2 = new Date(); long timeToResolveThisType = (date2.getTime()-date1.getTime()); timeSpendResolvingTypes += timeToResolveThisType; logger.info("Spent "+timeToResolveThisType+" ms to resolve "+type.getValue()+" with method "+m);*/ return returned; } public static class CrossReferencedEntitiesException extends Exception { } public static class UnresolvedTypeException extends FlexoException { private Type _unresolvedType; public UnresolvedTypeException(Type unresolvedType) { super("Cannot resolve " + unresolvedType); _unresolvedType = unresolvedType; } public Type getUnresolvedType() { return _unresolvedType; } } public static boolean isResolvable(Type type, DMModel dataModel, FJPDMSet context, FJPJavaSource source) { if (type == null) { logger.warning("isResolvable() called for null type"); return false; } if (!type.isResolved()) { return false; } TypeLookupInfo lookupInfo; try { lookupInfo = lookupEntity(type, dataModel, context, source, false); } catch (CrossReferencedEntitiesException e1) { lookupInfo = new TypeLookupInfo(); } if (lookupInfo.foundEntity != null) { return true; } else if (lookupInfo.resolvedButEntityNotAvailableYet) { return true; } else { logger.warning("Type: " + type + " is not resolvable"); return false; } } public static DMEntity resolveEntity(Type type, DMModel dataModel, FJPDMSet context, FJPJavaSource source, boolean importReferencedEntities) throws CrossReferencedEntitiesException { if (!type.isResolved()) { return null; } TypeLookupInfo lookupInfo = lookupEntity(type, dataModel, context, source, true); // Warn if not found if (lookupInfo.foundEntity == null) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Could not resolve " + type.getValue()); } } else { if (type instanceof DMType) { ((DMType) type).resolveAs(lookupInfo.foundEntity); } } return lookupInfo.foundEntity; } }