/** * 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.graph.query.parser; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.LoaderClassPath; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CharStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.stringtemplate.StringTemplateGroup; import org.openspotlight.common.util.ClassLoaderUtil; import org.openspotlight.common.util.ClassPathResource; import org.openspotlight.common.util.Sha1; import org.openspotlight.graph.exception.SLInvalidQuerySyntaxException; import org.openspotlight.graph.query.InvalidQuerySyntaxException; import org.openspotlight.graph.query.QueryTextInternal; import org.openspotlight.graph.query.SLQLVariable; /** * The Class SLQueryTextInternalBuilder. This class genarates, based on slql external dsl, a new instance SLQueryTextInternal. * * @author porcelli */ public class QueryTextInternalBuilder { private class CaseInsensitiveStringStream extends ANTLRStringStream { public CaseInsensitiveStringStream( final String input) { super(input); } @Override public int LA(final int i) { final int result = super.LA(i); if (result == 0) { return 0; // undefined } if (result == CharStream.EOF) { return CharStream.EOF; } return Character.toLowerCase(result); } } /** * The Enum SLQLVariableDataType. * * @author porcelli */ private enum SLQLVariableDataType { /** The BOOLEAN data type. */ BOOLEAN, /** The DECIMAL data type. */ DECIMAL, /** The INTEGER data type. */ INTEGER, /** The STRING data type. */ STRING } private CtClass[] CONSTRUCTOR_ARGS; private CtClass[] CONSTRUCTOR_THROWS; private CtClass[] EXECUTE_ARGS; private CtClass EXECUTE_RETURN_TYPE; private CtClass[] EXECUTE_THROWS; /** * Builds the query. * * @param id the id * @param variables the variables * @param outputModelName the output model name * @param stringConstants the string constants * @param target the target * @param executeContent the execute content * @return the sL query text internal * @throws SLInvalidQuerySyntaxException the SL invalid query syntax exception */ private QueryTextInternal buildQuery(final String id, final Set<SLQLVariable> variables, final String outputModelName, final Map<Integer, String> stringConstants, final QueryTextInternal target, final String executeContent) throws SLInvalidQuerySyntaxException { try { final String className = getClassName(id); if (!ClassLoaderUtil.existsClass(className)) { createNewQueryClass(className, executeContent); } @SuppressWarnings("unchecked") final Class<AbstractSLQueryTextInternal> queryResult = (Class<AbstractSLQueryTextInternal>) ClassLoaderUtil.getClass(className); Constructor<AbstractSLQueryTextInternal> constr; constr = queryResult.getConstructor(String.class, Set.class, String.class, QueryTextInternal.class, Map.class); return constr.newInstance(id, variables, outputModelName, target, stringConstants); } catch (final Exception e) { throw new InvalidQuerySyntaxException(e); } } /** * Builds the query info. * * @param slqlText the slql text * @return the sL query text internal info * @throws SLInvalidQuerySyntaxException the SL invalid query syntax exception */ private QueryTextInternalInfo buildQueryInfo(final String slqlText) throws SLInvalidQuerySyntaxException { try { final InputStream stream = ClassPathResource.getResourceFromClassPath(this.getClass(), "SLQLTemplate.stg"); final Reader reader = new InputStreamReader(stream); final StringTemplateGroup templates = new StringTemplateGroup(reader); reader.close(); final CaseInsensitiveStringStream inputStream = new CaseInsensitiveStringStream(slqlText); final SLQLLexer lex = new SLQLLexer(inputStream); final CommonTokenStream tokens = new CommonTokenStream(lex); final SLQLParser parser = new SLQLParser(tokens); parser.setIsTesting(false); if (parser.hasErrors()) { throw parser.getErrors().get(0); } final CommonTree result = (CommonTree) parser.compilationUnit().tree; final String uniqueId = Sha1.getSha1SignatureEncodedAsHexa(result.toStringTree().toLowerCase()); String targetUniqueId = null; if (parser.getDefineTargetTreeResult() != null) { targetUniqueId = Sha1.getSha1SignatureEncodedAsHexa(parser.getDefineTargetTreeResult()); } final CommonTreeNodeStream treeNodes = new CommonTreeNodeStream(result); final SLQLWalker walker = new SLQLWalker(treeNodes); walker.setTemplateLib(templates); final QueryTextInternalInfo queryInfo = walker.compilationUnit().queryInfoReturn; queryInfo.setId(uniqueId); queryInfo.setTargetUniqueId(targetUniqueId); return queryInfo; } catch (final Exception e) { throw new InvalidQuerySyntaxException(e); } } /** * Builds the target query. * * @param targetUniqueId the target unique id * @param defineTargetContent the define target content * @param stringConstants the string constants * @return the sL query text internal * @throws SLInvalidQuerySyntaxException the SL invalid query syntax exception */ private QueryTextInternal buildTargetQuery(final String targetUniqueId, final String defineTargetContent, final Map<Integer, String> stringConstants) throws SLInvalidQuerySyntaxException { try { final String className = getClassName(targetUniqueId); if (!ClassLoaderUtil.existsClass(className)) { createNewQueryClass(className, defineTargetContent); } @SuppressWarnings("unchecked") final Class<AbstractSLQueryTextInternal> queryResult = (Class<AbstractSLQueryTextInternal>) ClassLoaderUtil.getClass(className); Constructor<AbstractSLQueryTextInternal> constr; constr = queryResult.getConstructor(String.class, Set.class, String.class, QueryTextInternal.class, Map.class); return constr.newInstance(targetUniqueId, null, null, null, stringConstants); } catch (final Exception e) { throw new InvalidQuerySyntaxException(e); } } /** * Builds the variable collection. * * @param queryInfo the query info * @return the set< slql variable> */ private Set<SLQLVariable> buildVariableCollection(final QueryTextInternalInfo queryInfo) { final Set<SLQLVariable> result = new HashSet<SLQLVariable>(); final Collection<SLQLVariable> tempBoolVars = getVariablesByDataType(SLQLVariableDataType.BOOLEAN, queryInfo.getBoolVariables(), queryInfo.getMessageVariables(), queryInfo.getDomainVariables()); final Collection<SLQLVariable> tempIntVars = getVariablesByDataType(SLQLVariableDataType.INTEGER, queryInfo.getIntVariables(), queryInfo.getMessageVariables(), queryInfo.getDomainVariables()); final Collection<SLQLVariable> tempDecVars = getVariablesByDataType(SLQLVariableDataType.DECIMAL, queryInfo.getDecVariables(), queryInfo.getMessageVariables(), queryInfo.getDomainVariables()); final Collection<SLQLVariable> tempStringVars = getVariablesByDataType(SLQLVariableDataType.STRING, queryInfo.getStringVariables(), queryInfo.getMessageVariables(), queryInfo.getDomainVariables()); result.addAll(tempBoolVars); result.addAll(tempIntVars); result.addAll(tempDecVars); result.addAll(tempStringVars); return result; } /** * Creates the new query class. * * @param className the class name * @param executeContent the execute content * @throws SLInvalidQuerySyntaxException the SL invalid query syntax exception */ private void createNewQueryClass(final String className, final String executeContent) throws SLInvalidQuerySyntaxException { try { final ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(new LoaderClassPath(AbstractSLQueryTextInternal.class.getClassLoader())); final CtClass superClass = pool.get(AbstractSLQueryTextInternal.class.getName()); final CtClass clas = pool.makeClass(className, superClass); if (CONSTRUCTOR_ARGS == null) { for (final Constructor<?> constructor: AbstractSLQueryTextInternal.class.getConstructors()) { if (constructor.getParameterTypes().length > 0) { CONSTRUCTOR_ARGS = new CtClass[constructor.getParameterTypes().length]; CONSTRUCTOR_THROWS = new CtClass[constructor.getExceptionTypes().length]; for (int i = 0; i < constructor.getParameterTypes().length; i++) { CONSTRUCTOR_ARGS[i] = pool.get(constructor.getParameterTypes()[i].getName()); } for (int i = 0; i < constructor.getExceptionTypes().length; i++) { CONSTRUCTOR_THROWS[i] = pool.get(constructor.getExceptionTypes()[i].getName()); } break; } } for (final Method method: AbstractSLQueryTextInternal.class.getMethods()) { if (method.getName().equals("execute")) { EXECUTE_ARGS = new CtClass[method.getParameterTypes().length]; EXECUTE_THROWS = new CtClass[method.getExceptionTypes().length]; for (int i = 0; i < method.getParameterTypes().length; i++) { EXECUTE_ARGS[i] = pool.get(method.getParameterTypes()[i].getName()); } for (int i = 0; i < method.getExceptionTypes().length; i++) { EXECUTE_THROWS[i] = pool.get(method.getExceptionTypes()[i].getName()); } EXECUTE_RETURN_TYPE = pool.get(method.getReturnType().getName()); break; } } } final CtConstructor newConstructor = CtNewConstructor.make(CONSTRUCTOR_ARGS, CONSTRUCTOR_THROWS, clas); clas.addConstructor(newConstructor); final CtMethod newMethod = CtNewMethod.make(EXECUTE_RETURN_TYPE, "execute", EXECUTE_ARGS, EXECUTE_THROWS, executeContent, clas); clas.addMethod(newMethod); clas.toClass(QueryTextInternalBuilder.class.getClassLoader(), QueryTextInternalBuilder.class.getProtectionDomain()); } catch (final Exception e) { throw new InvalidQuerySyntaxException(e); } } /** * Gets the class name. * * @param id the id * @return the class name */ private String getClassName(final String id) { return "org.openspotlight.graph.query.SLQLQuery$A" + id; } /** * Gets the variables by data type. * * @param dataType the data type * @param variables the variables * @param messageVariables the message variables * @param domainVariables the domain variables * @return the variables by data type */ private Collection<SLQLVariable> getVariablesByDataType(final SLQLVariableDataType dataType, final Collection<String> variables, final Map<String, String> messageVariables, final Map<String, Set<Serializable>> domainVariables) { final Set<SLQLVariable> result = new HashSet<SLQLVariable>(variables.size()); for (final String activeVariableName: variables) { SLQLVariable variable = null; switch (dataType) { case INTEGER: variable = new SLQLVariableInteger(activeVariableName); break; case DECIMAL: variable = new SLQLVariableFloat(activeVariableName); break; case STRING: variable = new SLQLVariableString(activeVariableName); break; case BOOLEAN: variable = new SLQLVariableBoolean(activeVariableName); break; } if (messageVariables.containsKey(activeVariableName)) { variable.setDisplayMessage(messageVariables.get(activeVariableName)); } if (dataType != SLQLVariableDataType.BOOLEAN && domainVariables.containsKey(activeVariableName)) { variable.addAllDomainValue(domainVariables.get(activeVariableName)); } result.add(variable); } return result; } /** * Builds the SLQueryTextInternal based on input * * @param slqlText the slql text * @return the sL query text internal * @throws SLInvalidQuerySyntaxException the SL invalid query syntax exception */ public QueryTextInternal build(final String slqlText) throws SLInvalidQuerySyntaxException { final QueryTextInternalInfo queryInfo = buildQueryInfo(slqlText); QueryTextInternal target = null; if (queryInfo.hasTarget()) { target = buildTargetQuery(queryInfo.getTargetUniqueId(), queryInfo.getDefineTargetContent(), queryInfo.getStringsConstant()); } final Set<SLQLVariable> variables = buildVariableCollection(queryInfo); return buildQuery(queryInfo.getId(), variables, queryInfo.getOutputModelName(), queryInfo.getStringsConstant(), target, queryInfo.getContent()); } }