/* * Copyright 2014 JBoss Inc * * 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 org.artificer.repository.query; import org.artificer.common.ArtificerConstants; import org.artificer.common.ArtificerException; import org.artificer.common.query.xpath.ast.AbstractXPathNode; import org.artificer.common.query.xpath.ast.Argument; import org.artificer.common.query.xpath.ast.Expr; import org.artificer.common.query.xpath.ast.ForwardPropertyStep; import org.artificer.common.query.xpath.ast.Predicate; import org.artificer.common.query.xpath.ast.PrimaryExpr; import org.artificer.common.query.xpath.visitors.XPathVisitor; import org.artificer.repository.ClassificationHelper; import org.artificer.repository.i18n.Messages; import javax.xml.namespace.QName; import java.net.URI; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; /** * @author Brett Meyer. */ public abstract class AbstractArtificerQueryVisitor implements XPathVisitor { protected static final QName CLASSIFIED_BY_ANY_OF = new QName(ArtificerConstants.SRAMP_NS, "classifiedByAnyOf"); protected static final QName CLASSIFIED_BY_ALL_OF = new QName(ArtificerConstants.SRAMP_NS, "classifiedByAllOf"); protected static final QName EXACTLY_CLASSIFIED_BY_ANY_OF = new QName(ArtificerConstants.SRAMP_NS, "exactlyClassifiedByAnyOf"); protected static final QName EXACTLY_CLASSIFIED_BY_ALL_OF = new QName(ArtificerConstants.SRAMP_NS, "exactlyClassifiedByAllOf"); protected static final QName GET_RELATIONSHIP_ATTRIBUTE = new QName(ArtificerConstants.SRAMP_NS, "getRelationshipAttribute"); protected static final QName GET_TARGET_ATTRIBUTE = new QName(ArtificerConstants.SRAMP_NS, "getTargetAttribute"); protected static final QName MATCHES = new QName("http://www.w3.org/2005/xpath-functions", "matches"); protected static final QName NOT = new QName("http://www.w3.org/2005/xpath-functions", "not"); protected static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd"); protected ClassificationHelper classificationHelper; protected ArtificerException error; protected AbstractArtificerQueryVisitor(ClassificationHelper classificationHelper) { this.classificationHelper = classificationHelper; } /** * @see org.artificer.common.query.xpath.visitors.XPathVisitor#visit(org.artificer.common.query.xpath.ast.Argument) */ @Override public void visit(Argument node) { if (node.getPrimaryExpr() != null) node.getPrimaryExpr().accept(this); else throw new RuntimeException(Messages.i18n.format("XP_ONLY_PRIMARY_FUNC_ARGS")); } /** * @see org.artificer.common.query.xpath.visitors.XPathVisitor#visit(org.artificer.common.query.xpath.ast.ArtifactSet) */ @Override public void visit(org.artificer.common.query.xpath.ast.ArtifactSet node) { node.getLocationPath().accept(this); } /** * @see org.artificer.common.query.xpath.visitors.XPathVisitor#visit(org.artificer.common.query.xpath.ast.Expr) */ @Override public void visit(Expr node) { node.getAndExpr().accept(this); } /** * @see org.artificer.common.query.xpath.visitors.XPathVisitor#visit(org.artificer.common.query.xpath.ast.Predicate) */ @Override public void visit(Predicate node) { node.getExpr().accept(this); } /** * Resolves the list of arguments to a collection of classification URIs. * @param arguments */ protected Collection<URI> resolveArgumentsToClassifications(List<Argument> arguments) { Collection<String> classifiedBy = new HashSet<>(); for (int idx = 1; idx < arguments.size(); idx++) { Argument arg = arguments.get(idx); if (arg.getPrimaryExpr() == null || arg.getPrimaryExpr().getLiteral() == null) { throw new RuntimeException(Messages.i18n.format("XP_INVALID_CLASSIFIER_FORMAT")); } classifiedBy.add(arg.getPrimaryExpr().getLiteral()); } try { return this.classificationHelper.resolveAll(classifiedBy); } catch (ArtificerException e) { this.error = e; return Collections.emptySet(); } } /** * Reduces an Argument subtree to the final {@link org.artificer.common.query.xpath.ast.ForwardPropertyStep} that is it's (supposed) * final node. This method will throw a runtime exception if it doesn't find the expected * {@link org.artificer.common.query.xpath.ast.ForwardPropertyStep}. * * @param argument */ protected ForwardPropertyStep reducePropertyArgument(Argument argument) { try { ForwardPropertyStep fps = (ForwardPropertyStep) argument.getExpr().getAndExpr().getLeft().getLeft().getLeft(); if (fps == null) { throw new NullPointerException(); } return fps; } catch (Throwable t) { throw new RuntimeException(Messages.i18n.format("XP_EXPECTED_PROPERTY_ARG")); } } /** * Returns true if the Argument subtree's final node is the full-text seach wildcard. * Example: /s-ramp[xp2:matches(., '.*foo.*')] The primary expression will be the xpath value (".") used for free-text * searches. * * @param argument */ protected boolean isFullTextSearch(Argument argument) { AbstractXPathNode node = argument.getExpr().getAndExpr().getLeft().getLeft().getLeft(); if (node instanceof PrimaryExpr) { PrimaryExpr primaryExpr = (PrimaryExpr) node; return primaryExpr.getXpathValue().equals("."); } return false; } /** * Reduces an Argument to a string literal. This method will throw a runtime exception if it * doesn't find the expected string literal. * @param argument */ protected String reduceStringLiteralArgument(Argument argument) { try { String l = argument.getPrimaryExpr().getLiteral(); if (l == null) { throw new NullPointerException(); } return l; } catch (Throwable t) { throw new RuntimeException(Messages.i18n.format("XP_EXPECTED_STRING_LITERAL_ARG")); } } /** * Escape string literals to prevent injection. * @param literal */ protected String escapeStringLiteral(String literal) { return literal.replace("'", "''"); } private int uniqueArtifactCounter = 1; private int uniqueRelationshipCounter = 1; private int uniqueTargetCounter = 1; protected String newArtifactAlias() { return "artifact" + uniqueArtifactCounter++; } protected String newRelationshipAlias() { return "relationship" + uniqueRelationshipCounter++; } protected String newTargetAlias() { return "target" + uniqueTargetCounter++; } }