/** * OpenSpotLight - Open Source IT Governance Platform * * Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA * or third-party contributors as indicated by the @author tags or express * copyright attribution statements applied by the authors. All third-party * contributions are distributed under license by CARAVELATECH CONSULTORIA E * TECNOLOGIA EM INFORMATICA LTDA. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * *********************************************************************** * OpenSpotLight - Plataforma de Governança de TI de Código Aberto * * Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA * EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta * @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor. * Todas as contribuições de terceiros estão distribuídas sob licença da * CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA. * * Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os * termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software * Foundation. * * Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA * GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA * FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes. * * Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este * programa; se não, escreva para: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.openspotlight.bundle.language.java.resolver; import org.openspotlight.bundle.common.SLBundleException; import org.openspotlight.bundle.language.java.resolver.TypeResolver.IncludedResult; import org.openspotlight.bundle.language.java.resolver.TypeResolver.ResultOrder; import org.openspotlight.graph.GraphReaderorg.openspotlight.graph.SLLink; import org.openspotlight.graph.Node; import org.openspotlight.graph.exception.NodeNotFoundException; import org.openspotlight.graph.exception.SLPropertyNotFoundException; import org.openspotlight.graph.query.SLInvalidQueryElementException; import org.openspotlight.graph.query.SLInvalidQuerySyntaxException; import org.openspotlight.graph.query.SLQueryApi; import org.openspotlight.graph.query.SLQueryResult; import java.util.*; import java.util.Map.Entry; import static org.openspotlight.common.util.Assertions.checkNotEmpty; import static org.openspotlight.common.util.Assertions.checkNotNull; /** * This is a support class that resolves the methods. * * @author porcelli */ public class MethodResolver<T extends Node, M extends Node> { /** The type resolver. */ private TypeResolver<T> typeResolver = null; /** The graph session. */ private GraphReadGraphReader = null; /** Class that defines the method super type. */ private Class<? extends Node> methodSuperType = null; /** Class that defines the link between type and method. */ private Class<? extends SLLink> typeMethodLink = null; /** Class that defines the link between method and its parameter types. */ private Class<? extends SLLink> methodParameterDefinitionLink = null; /** The property name that defines the method simple name. */ private String propertySimpleMethodName = null; /** The property name that defines the order of method parameter definition. */ private String propertyMethodDefinitionOrderName = null; /** The cache for fastest method resolution . */ private Map<String, String> cache = null; /** * Instantiates a new method resolver. * * @param typeResolver the type resolver * @param graphSession the graph session * @param methodSuperType class that defines the method super type * @param typeMethodLink class that defines the link between type and method * @param methodParameterDefinitionLink class that defines the link between method and its parameter types * @param propertySimpleMethodName property name that defines the order of method parameter definition * @param propertyMethodDefinitionOrderName property name that defines the order of method parameter definition */ public MethodResolver( final AbstractTypeResolver<T> typeResolver, final GraphReader graphSGraphReader final Class<? extends Node> methodSuperType, final Class<? extends SLLink> typeMethodLink, final Class<? extends SLLink> methodParameterDefinitionLink, final String propertySimpleMethodName, final String propertyMethodDefinitionOrderName ) { checkNotNull("typeResolver", typeResolver); checkNotNull("graphSession", graphSession); checkNotNull("methodSuperType", methodSuperType); checkNotNull("typeMethodLink", typeMethodLink); checkNotNull("methodParameterDefinitionLink", methodParameterDefinitionLink); checkNotEmpty("propertySimpleMethodName", propertySimpleMethodName); checkNotEmpty("propertyMethodDefinitionOrderName", propertyMethodDefinitionOrderName); this.typeResolver = typeResolver; this.graphSession = graphSession; this.methodSuperType = methodSuperType; this.typeMethodLink = typeMethodLink; this.methodParameterDefinitionLink = methodParameterDefinitionLink; this.propertySimpleMethodName = propertySimpleMethodName; this.propertyMethodDefinitionOrderName = propertyMethodDefinitionOrderName; this.cache = new HashMap<String, String>(); } /** * Caches the found method. * * @param type the type * @param methodName the method name * @param paramTypes the param types * @param foundMethod the found method * @return the found method * @throws SLBundleException */ private <XM extends M> XM cacheFoundMethod( final T type, final String methodName, final List<T> paramTypes, final XM foundMethod ) { final String cachedId = this.getUniqueId(type, methodName, paramTypes); this.cache.put(cachedId, foundMethod.getID()); return foundMethod; } /** * Returns the best match based on param types. * * @param methods the found methods that has the same number of parameters and match the types * @param paramTypes the param types * @return the best match * @throws SLBundleException if method not found */ public <XM extends M> XM getBestMatch( final Map<XM, T[]> methods, final List<T> paramTypes ) { final Map<XM, T[]> possibleMethods = methods; int maxLooping = methods.size() + 1; int loopCount = 0; while (true) { loopCount++; if (loopCount == maxLooping) { throw new SLBundleException(); } final Iterator<Entry<XM, T[]>> methodIterator = possibleMethods.entrySet().iterator(); final Entry<XM, T[]> firstMethod = methodIterator.next(); if (!methodIterator.hasNext()) { return firstMethod.getKey(); } final Entry<XM, T[]> secondMethod = methodIterator.next(); for (int i = 0; i < paramTypes.size(); i++) { final T activeParam = paramTypes.get(i); final T firstMethodParam = firstMethod.getValue()[i]; final T secondMethodParam = secondMethod.getValue()[i]; final BestTypeMatch bestTypeResult = this.typeResolver.bestMatch(activeParam, firstMethodParam, secondMethodParam); if (bestTypeResult == BestTypeMatch.T1) { methods.remove(secondMethod.getKey()); maxLooping = methods.size() + 1; loopCount = 0; } } } } /** * Gets the cache. * * @return the cache */ Map<String, String> getCache() { return this.cache; } /** * Gets the cached data. * * @param type the type * @param methodName the method name * @param paramTypes the param types * @return the cached result or null if it is not cached */ @SuppressWarnings( "unchecked" ) private <XM extends M> XM getCachedData( final T type, final String methodName, final List<T> paramTypes ) { String cachedId = ""; try { cachedId = this.getUniqueId(type, methodName, paramTypes); final String id = this.cache.get(cachedId); if (id != null) { return (XM)this.graphSession.getNodeByID(id); } } catch (final Exception e) { this.cache.remove(cachedId); } return null; } /** * Returns the method based on type (that declares the method) and method name * * @param type the type * @param methodName the method name * @return the found method * @throws SLBundleException if method not found. * @throws SLInvalidQuerySyntaxException */ public <XM extends M> XM getMethod( final T type, final String methodName ) throws SLInvalidQuerySyntaxException, SLInvalidQueryElementException { return this.<XM>getMethod(type, methodName, null); } /** * Returns the method based on type (that declares the method), method name and its param types. * * @param type the type * @param methodName the method name * @param paramTypes the param types * @return the method * @throws SLBundleException if method not found. * @throws SLInvalidQuerySyntaxException */ @SuppressWarnings( "unchecked" ) public <XM extends M> XM getMethod( final T type, final String methodName, final List<T> paramTypes ) throws SLInvalidQuerySyntaxException, SLInvalidQueryElementException { checkNotNull("type", type); checkNotEmpty("methodName", methodName); if (!this.isContreteType(type)) { throw new IllegalArgumentException("INVALID PARAMETER"); } int paramSize = 0; if (paramTypes != null) { if (!this.isContreteType(paramTypes)) { throw new IllegalArgumentException("INVALID PARAMETER"); } paramSize = paramTypes.size(); } final XM chachedMethod = this.<XM>getCachedData(type, methodName, paramTypes); if (chachedMethod != null) { return chachedMethod; } final List<T> typeHierarchy = this.typeResolver.getAllParents(type, ResultOrder.DESC, IncludedResult.INCLUDE_ACTUAL_TYPE_ON_RESULT); final SLQueryApi query = this.graphSession.createQueryApi(); query.select().type(this.methodSuperType.getName()).subTypes().comma().byLink(this.typeMethodLink.getName()).b().selectEnd().select().type( this.methodSuperType.getName()).subTypes().selectEnd().where().type( this.methodSuperType.getName()).subTypes().each().link( this.methodParameterDefinitionLink.getName()).a().count().equalsTo().value( paramSize).and().each().property( this.propertySimpleMethodName).equalsTo().value( methodName).typeEnd().whereEnd(); for (final T activeType : typeHierarchy) { final List<T> inputType = new LinkedList<T>(); inputType.add(activeType); final SLQueryResult result = query.execute((Collection<Node>)inputType); // FIXME: where cast bug fixed..: REMOVE THIS! final List<XM> validMethods = new LinkedList<XM>(); for (final Node activeMethod : result.getNodes()) { try { validMethods.add((XM)this.graphSession.getNodeByID(activeMethod.getID())); } catch (NodeNotFoundException e) { throw new SLBundleException("", e); } } if (paramSize == 0 && validMethods.size() > 0) { return this.cacheFoundMethod(type, methodName, paramTypes, validMethods.get(0)); } final Map<XM, T[]> possibleMethods = new TreeMap<XM, T[]>(); // Now checks for paramTypes for (final XM activeMethod : validMethods) { final T[] orderedParams = (T[])new Node[paramSize]; final Collection<? extends SLLink> links = this.graphSession.getUnidirectionalLinksBySource( this.methodParameterDefinitionLink, activeMethod); for (final SLLink methodParameterDefinition : links) { try { orderedParams[methodParameterDefinition.getProperty(Integer.class, this.propertyMethodDefinitionOrderName).getValue()] = (T)methodParameterDefinition.getTarget(); } catch (SLPropertyNotFoundException e) { throw new SLBundleException("Property Method Definition Order not found.", e); } } boolean isValidMethod = true; for (int i = 0; i < orderedParams.length; i++) { final T activeParamType = orderedParams[i]; if (!this.typeResolver.isTypeOf(paramTypes.get(i), activeParamType)) { isValidMethod = false; break; } } if (isValidMethod) { possibleMethods.put(activeMethod, orderedParams); } } if (possibleMethods.size() == 1) { return this.cacheFoundMethod(type, methodName, paramTypes, possibleMethods.entrySet().iterator().next().getKey()); } if (possibleMethods.size() > 0) { return this.cacheFoundMethod(type, methodName, paramTypes, this.getBestMatch(possibleMethods, paramTypes)); } } throw new SLBundleException(); } /** * Gets the unique id based on parameters. * * @param type the type * @param methodName the method name * @param paramTypes the param types * @return the unique id */ String getUniqueId( final T type, final String methodName, final List<T> paramTypes ) { final StringBuilder sb = new StringBuilder(); sb.append(type.getID()); sb.append(":"); sb.append(methodName); sb.append(":"); if (paramTypes != null) { for (final T t : paramTypes) { sb.append(t.getID()); sb.append(":"); } } return sb.toString(); } /** * Checks if is contrete type. * * @param types the types * @return true, if is contrete type */ private boolean isContreteType( final List<T> types ) { for (final T t : types) { if (!(this.typeResolver.isConcreteType(t) || this.typeResolver.isPrimitiveType(t))) { return false; } } return true; } /** * Checks if is contrete type. * * @param type the type * @return true, if is contrete type */ private boolean isContreteType( final T type ) { return this.typeResolver.isConcreteType(type); } }