/* * Copyright 2009-2016 the original author or authors. * * 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.codehaus.groovy.eclipse.dsl.pointcuts; import groovy.lang.Closure; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import org.codehaus.groovy.eclipse.GroovyLogManager; import org.codehaus.groovy.eclipse.TraceCategory; import org.codehaus.groovy.eclipse.dsl.GroovyDSLCoreActivator; import org.codehaus.groovy.eclipse.dsl.contributions.DSLContributionGroup; import org.codehaus.groovy.eclipse.dsl.contributions.IContributionGroup; import org.codehaus.groovy.eclipse.dsl.pointcuts.impl.AndPointcut; import org.codehaus.groovy.eclipse.dsl.pointcuts.impl.NotPointcut; import org.codehaus.groovy.eclipse.dsl.pointcuts.impl.OrPointcut; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IStorage; /** * Abstract implementation of the Pointcut. Most concrete instances will only * accept one argument. * @author andrew * @created Nov 17, 2010 */ public abstract class AbstractPointcut implements IPointcut { private IStorage containerIdentifier; private StringObjectVector elements = new StringObjectVector(1); private IProject project; private String pointcutName; public AbstractPointcut(IStorage containerIdentifier, String pointcutName) { this.containerIdentifier = containerIdentifier; this.pointcutName = pointcutName; } public String getPointcutName() { return pointcutName; } public void setPointcutName(String pointcutName) { this.pointcutName = pointcutName; } public String getPointcutDebugName() { return pointcutName + " (" + getClass().getSimpleName() + ")"; } public IStorage getContainerIdentifier() { return containerIdentifier; } public void setContainerIdentifier(IStorage containerIdentifier) { this.containerIdentifier = containerIdentifier; } public void verify() throws PointcutVerificationException { // most pointcuts can't have more than one argument if (elements.size > 1) { throw new PointcutVerificationException("Can't have more than one argument to this pointcut", this); } } public final void addArgument(Object argument) { elements.add(null, argument); } /** * Attempt a match on the given object to match and return all the matches found * @param pattern pattern to match * @param toMatch object to match on * @return collection of objects matched, or null if no matches found */ public abstract Collection<?> matches(GroovyDSLDContext pattern, Object toMatch); public final void addArgument(String name, Object argument) { if (name == null) { addArgument(argument); return; } elements.add(name, argument); } /** * matches on the pointct passed in and also * binds the argument to the argument name if * a name exists. * * Must call if the argument to this pointcut is another pointcut * @param argument * @param pattern * @return */ protected Collection<?> matchOnPointcutArgument( IPointcut argument, GroovyDSLDContext pattern, Collection<?> allElementsToMatch) { if (allElementsToMatch == null) { return null; } Collection<Object> outerResults = new LinkedHashSet<Object>(); for (Object toMatch : allElementsToMatch) { Collection<?> innerResults = argument.matches(pattern, toMatch); if (innerResults != null) { String bindingName = getArgumentName(argument); if (bindingName != null) { pattern.addToBinding(bindingName, innerResults); } outerResults.add(toMatch); } } // return null if no matches found return outerResults.size() > 0 ? outerResults : null; } /** * Variant of matchOnPointcutArgument here. pass through the * bound results to the containing pointcut */ protected Collection<?> matchOnPointcutArgumentReturnInner( IPointcut argument, GroovyDSLDContext pattern, Collection<?> allElementsToMatch) { String bindingName = getArgumentName(argument); Collection<Object> innerResults = new HashSet<Object>(); for (Object toMatch : allElementsToMatch) { Collection<?> tempInnerResults = argument.matches(pattern, toMatch); if (tempInnerResults != null) { innerResults.addAll(tempInnerResults); } } if (bindingName != null && innerResults.size() > 0) { pattern.addToBinding(bindingName, innerResults); } // return null if no matches found return innerResults != null && innerResults.size() > 0 ? innerResults : null; } /** * flattens a map of collections into a single collection * @param pointcutResult * @return */ protected Collection<?> flatten(Map<Object, Collection<?>> pointcutResult) { Collection<Object> newCollection = new HashSet<Object>(pointcutResult.size()); for (Collection<?> collection : pointcutResult.values()) { newCollection.addAll(collection); } return newCollection; } public final Object getFirstArgument() { if (elements.size > 0) { return elements.elementAt(0); } else { return null; } } public final String[] getArgumentNames() { return elements.getNames(); } public final Object[] getArgumentValues() { return elements.getElements(); } /** * returns the associated name for the given argument. * null if this is not an argument and null if there * is no name * @param argument * @return */ public final String getArgumentName(Object argument) { for (int i = 0; i < elements.size; i++) { if (elements.elementAt(i) == argument) { return elements.nameAt(i); } } return null; } public final String getFirstArgumentName() { if (elements.size > 0) { return elements.nameAt(0); } else { return null; } } public final String getNameForArgument(Object arg) { return elements.nameOf(arg); } public IPointcut normalize() { for (int i = 0; i < elements.size; i++) { Object elt = elements.elementAt(i); if (elt instanceof IPointcut) { elements.setElement(((IPointcut) elt).normalize(), i); } } return this; } public boolean fastMatch(GroovyDSLDContext pattern) { for (Object elt : elements.getElements()) { if (elt instanceof IPointcut && ! ((IPointcut) elt).fastMatch(pattern)) { return false; } } return true; } public void setProject(IProject project) { this.project = project; } public void accept(Closure contributionGroupClosure) { IContributionGroup group = new DSLContributionGroup(contributionGroupClosure); if (project != null) { try { // verify correctness of this pointcut this.verify(); // project is set to null inside of normalize, so cache it here IProject p = project; // now perform some optimizations on it // potentially creates a copy of the original pointcut IPointcut normalized = this.normalize(); if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Registering pointcut:\n" + this.toString()); } // register this pointcut and group for the given project GroovyDSLCoreActivator.getDefault().getContextStoreManager() .getDSLDStore(p).addContributionGroup(normalized, group); } catch (PointcutVerificationException e) { if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Ignoring invalid pointcut"); GroovyLogManager.manager.log(TraceCategory.DSL, e.getPointcutMessage()); GroovyLogManager.manager.logException(TraceCategory.DSL, e); } } } } /** * A standard verification that checks to see that all args are pointcuts. * @return null if all args are pointcuts, error status otherwise * @throws PointcutVerificationException */ protected final String allArgsArePointcuts() throws PointcutVerificationException { for (Object arg : elements.getElements()) { if (arg == null) { continue; } if (! (arg instanceof IPointcut)) { return "All arguments should be pointcuts"; } else { ((IPointcut) arg).verify(); } } return null; } /** * A standard verification that checks to see the number of args * @arg num * @return null if number of args matches num, error message otherwise */ protected final String matchesArgNumber(int num) { Object[] elements2 = elements.getElements(); if (elements2.length == num) { return null; } else { return "Expecting " + num + " arguments, but found " + elements2.length; } } /** * A standard verification that checks to see the number of args * @arg num * @return a string if number of args is not 1 else null */ protected final String hasOneArg() { if (elements.getElements().length == 1) { return null; } else { return "Expecting 1 argument, but found " + elements.getElements().length + ". Consider using '&' or '|' to connect arguments."; } } /** * A standard verification that checks to see the number of args * @arg num * @return a string if number of args is not 1 or 0 else null */ protected final String hasOneOrNoArgs() { if (elements.getElements().length <= 1) { return null; } else { return "Expecting 1 or no arguments, but found " + elements.getElements().length + ". Consider using '&' or '|' to connect arguments."; } } protected final String hasNoArgs() { if (elements.getElements().length == 0) { return null; } else { return "Expecting no arguments, but found " + elements.getElements().length + ". Consider using '&' or '|' to connect arguments."; } } protected final String allArgsAreStrings() { for (Object arg : elements.getElements()) { if (arg == null) { continue; } if (! (arg instanceof String)) { return "All arguments should be strings"; } } return null; } protected final String oneStringOrOnePointcutArg() throws PointcutVerificationException { String maybeStatus = allArgsAreStrings(); String maybeStatus2 = allArgsArePointcuts(); if (maybeStatus != null && maybeStatus2 != null) { return "This pointcut supports exactly one argument of type Pointcut or String. Consider using '&' or '|' to connect arguments."; } maybeStatus = hasOneArg(); if (maybeStatus != null) { return maybeStatus; } return null; } protected final String oneStringOrOnePointcutOrOneClassArg() throws PointcutVerificationException { String maybeStatus = allArgsAreStrings(); String maybeStatus2 = allArgsArePointcuts(); String maybeStatus3 = allArgsAreClasses(); if (maybeStatus != null && maybeStatus2 != null && maybeStatus3 != null) { return "This pointcut supports exactly one argument of type Pointcut or String or Class. Consider using '&' or '|' to connect arguments."; } maybeStatus = hasOneArg(); if (maybeStatus != null) { return maybeStatus; } return null; } protected final String allArgsAreClasses() { for (Object arg : elements.getElements()) { if (arg == null) { continue; } if (!(arg instanceof Class)) { return "All arguments should be classes"; } } return null; } protected IPointcut and(IPointcut other) { AbstractPointcut andPointcut = new AndPointcut(containerIdentifier, "and"); andPointcut.setProject(project); andPointcut.addArgument(this); andPointcut.addArgument(other); return andPointcut; } protected IPointcut or(IPointcut other) { AbstractPointcut orPointcut = new OrPointcut(containerIdentifier, "or"); orPointcut.setProject(project); orPointcut.addArgument(this); orPointcut.addArgument(other); return orPointcut; } protected IPointcut bitwiseNegate() { AbstractPointcut notPointcut = new NotPointcut(containerIdentifier, "not"); notPointcut.setProject(project); notPointcut.addArgument(this); return notPointcut; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("(" + containerIdentifier + ")\n"); formatedString(sb, 2); return sb.toString(); } protected void formatedString(StringBuilder sb, int indent) { sb.append(getPointcutDebugName()); elements.formattedString(sb, indent+2); sb.append("\n"); } static String spaces(int indent) { StringBuilder sb = new StringBuilder(indent+2); for (int i = 0; i < indent; i++) { sb.append(' '); } return sb.toString(); } protected Map<String, Object> namedArgumentsAsMap() { return elements.asMap(); } protected Collection<?> ensureCollection(Object toMatch) { if (toMatch == null) { return null; } return toMatch instanceof Collection ? (Collection<?>) toMatch : Collections.singleton(toMatch); } }