/* * Copyright 2015 by Thomas Lottermann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package notaql.parser; import notaql.datamodel.AtomValue; import notaql.engines.Engine; import notaql.engines.EngineEvaluator; import notaql.engines.EngineService; import notaql.model.AttributeSpecification; import notaql.model.EvaluationException; import notaql.model.Transformation; import notaql.model.path.OutputPath; import notaql.model.predicate.Predicate; import notaql.model.vdata.ObjectVData; import notaql.model.vdata.VData; import notaql.model.vdata.aggregation.AggregatingObjectVData; import notaql.parser.antlr.NotaQL2Parser; import org.antlr.v4.runtime.misc.NotNull; import java.util.*; /** * Created by thomas on 08.01.15. */ public class TransformationParser { private final NotaQL2Parser.TransformationContext transformationContext; private final EngineEvaluator inEngineEvaluator; private final EngineEvaluator outEngineEvaluator; private final AtomValueVisitor atomValueVisitor; private final InputPathVisitor inputPathVisitor; private final OutputPathVisitor outputPathVisitor; private final PredicateVisitor predicateVisitor; private final VDataVisitor vDataVisitor; public TransformationParser(NotaQL2Parser.TransformationContext transformationContext) { this.transformationContext = transformationContext; // create all visitors necessary to parse stuff this.atomValueVisitor = new AtomValueVisitor(this); this.inputPathVisitor = new InputPathVisitor(this); this.outputPathVisitor = new OutputPathVisitor(this); this.predicateVisitor = new PredicateVisitor(this); this.vDataVisitor = new VDataVisitor(this); // get the engines this.inEngineEvaluator = getEngineEvaluator(transformationContext.inEngine().engine()); this.outEngineEvaluator = getEngineEvaluator(transformationContext.outEngine().engine()); } public Transformation parse() { Predicate inPredicate = null; if(transformationContext.inPredicate() != null) { inPredicate = predicateVisitor.visit(transformationContext.inPredicate().predicate()); } final List<AttributeSpecification> specifications = new LinkedList<>(); for (NotaQL2Parser.AttributeSpecificationContext specificationCtx : transformationContext.attributeSpecification()) { final OutputPath outputPath = getOutputPathVisitor().visit(specificationCtx.genericOutputPath()); final VData vData = getVDataVisitor().visit(specificationCtx.vData()); appendObjectifiedPath(outputPath, vData, specifications); } return new Transformation(inPredicate, null, getEngineEvaluator(transformationContext.inEngine().engine()), getEngineEvaluator(transformationContext.outEngine().engine()), specifications); } /** * Objectify the paths and then append it to the specifications (or replace) * @param path * @param vData * @param specifications */ private void appendObjectifiedPath(OutputPath path, VData vData, List<AttributeSpecification> specifications) { final AttributeSpecification specification = objectifyPath(path, vData, specifications); // look if we already aggregated something of this name final Optional<AttributeSpecification> optionalSpecification = specifications .stream() .filter(s -> s.getOutputPath().getPathSteps().get(0).equals(path.getPathSteps().get(0))) .findAny(); if(optionalSpecification.isPresent()) { Collections.replaceAll(specifications, optionalSpecification.get(), specification); return; } specifications.add(specification); } /** * This ensures to reduce the steps in the output paths to one * * @param path * @param vData * @return */ private AttributeSpecification objectifyPath(OutputPath path, VData vData, List<AttributeSpecification> specifications) { int size = path.getPathSteps().size(); if(size > 1) { final OutputPath subPath = new OutputPath(new LinkedList<>(path.getPathSteps().subList(1, size))); final AttributeSpecification subSpecification; final LinkedList<AttributeSpecification> nestedSpecifications; // look if we already aggregated something of this name final Optional<AttributeSpecification> optionalSpecification = specifications .stream() .filter(s -> s.getOutputPath().getPathSteps().get(0).equals(path.getPathSteps().get(0))) .findAny(); // in case we did: make sure it is an AggregatingObjectVData and then remember the old specifications if(optionalSpecification.isPresent()) { if(!(optionalSpecification.get().getVData() instanceof AggregatingObjectVData)) throw new EvaluationException("The output path '"+ path.toString() + "' clashes with the same one."); nestedSpecifications = new LinkedList<>(((AggregatingObjectVData) optionalSpecification.get().getVData()).getSpecifications()); subSpecification = objectifyPath(subPath, vData, nestedSpecifications); } else { nestedSpecifications = new LinkedList<>(); subSpecification = objectifyPath(subPath, vData, Collections.<AttributeSpecification>emptyList()); } // Construct the new Aggregating VData nestedSpecifications.add(subSpecification); final AggregatingObjectVData resultingVData = new AggregatingObjectVData(nestedSpecifications); final OutputPath reducedPath = new OutputPath(path.getPathSteps().get(0)); return new AttributeSpecification(reducedPath, resultingVData); } return new AttributeSpecification(path, vData); } private EngineEvaluator getEngineEvaluator(@NotNull NotaQL2Parser.EngineContext ctx) { final HashMap<String, AtomValue<?>> params = new HashMap<>(); for (int i = 0; i < ctx.atom().size(); i++) { final String key = ctx.Name(i + 1).getText(); final AtomValue<?> value = getAtomValueVisitor().visit(ctx.atom(i)); params.put(key, value); } return EngineService.getInstance().getEngine(ctx.engineName.getText()).createEvaluator(this, params); } public EngineEvaluator getInEngineEvaluator() { return inEngineEvaluator; } public EngineEvaluator getOutEngineEvaluator() { return outEngineEvaluator; } public AtomValueVisitor getAtomValueVisitor() { return atomValueVisitor; } public InputPathVisitor getInputPathVisitor() { return inputPathVisitor; } public OutputPathVisitor getOutputPathVisitor() { return outputPathVisitor; } public PredicateVisitor getPredicateVisitor() { return predicateVisitor; } public VDataVisitor getVDataVisitor() { return vDataVisitor; } }