001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.core.event;
019:
020: import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
021: import org.apache.ivy.plugins.matcher.Matcher;
022: import org.apache.ivy.plugins.matcher.PatternMatcher;
023: import org.apache.ivy.util.filter.AndFilter;
024: import org.apache.ivy.util.filter.Filter;
025: import org.apache.ivy.util.filter.NoFilter;
026: import org.apache.ivy.util.filter.NotFilter;
027: import org.apache.ivy.util.filter.OrFilter;
028:
029: /**
030: * A filter implementation filtering {@link IvyEvent} based upon an event name and a filter
031: * expression. The name will be matched against the event name using the {@link PatternMatcher} used
032: * to construct this object. The filter expression is a string describing how the event should be
033: * filtered according to its attributes values. The matching between the filter values and the event
034: * attribute values is done using the {@link PatternMatcher} used to construct this object. Here are
035: * some examples: <table>
036: * <tr>
037: * <td>expression</td>
038: * <td>effect</td>
039: * </tr>
040: * <tr>
041: * <td>type=zip</td>
042: * <td>accepts event with a type attribute matching zip</td>
043: * </tr>
044: * <tr>
045: * <td>type=zip,jar</td>
046: * <td>accepts event with a type attribute matching zip or jar</td>
047: * </tr>
048: * <tr>
049: * <td>type=src AND ext=zip</td>
050: * <td>accepts event with a type attribute matching src AND an ext attribute matching zip</td>
051: * </tr>
052: * <tr>
053: * <td>type=src OR ext=zip</td>
054: * <td>accepts event with a type attribute matching src OR an ext attribute matching zip</td>
055: * </tr>
056: * <tr>
057: * <td>NOT type=src</td>
058: * <td>accepts event with a type attribute NOT matching src</td>
059: * </tr>
060: * </table> Combination of these can be used, but no parentheses are supported right now, so only
061: * the default priority can be used. The priority order is this one: AND OR NOT = This means that
062: * artifact=foo AND ext=zip OR type=src will match event with artifact matching foo AND (ext
063: * matching zip OR type matching src)
064: *
065: * @since 1.4
066: */
067: public class IvyEventFilter implements Filter {
068: private static final String NOT = "NOT ";
069:
070: private static final String OR = " OR ";
071:
072: private static final String AND = " AND ";
073:
074: private PatternMatcher matcher;
075:
076: private Filter nameFilter;
077:
078: private Filter attFilter;
079:
080: public IvyEventFilter(String event, String filterExpression,
081: PatternMatcher matcher) {
082: this .matcher = matcher == null ? ExactPatternMatcher.INSTANCE
083: : matcher;
084: if (event == null) {
085: nameFilter = NoFilter.INSTANCE;
086: } else {
087: final Matcher eventNameMatcher = this .matcher
088: .getMatcher(event);
089: nameFilter = new Filter() {
090: public boolean accept(Object o) {
091: IvyEvent e = (IvyEvent) o;
092: return eventNameMatcher.matches(e.getName());
093: }
094: };
095: }
096: attFilter = filterExpression == null
097: || filterExpression.trim().length() == 0 ? NoFilter.INSTANCE
098: : parseExpression(filterExpression);
099: }
100:
101: private Filter parseExpression(String filterExpression) {
102: // expressions handled for the moment: (informal grammar)
103: // EXP := SIMPLE_EXP | AND_EXP | OR_EXP | NOT_EXP
104: // AND_EXP := EXP && EXP
105: // OR_EXP := EXP || EXP
106: // NOT_EXP := ! EXP
107: // SIMPLE_EXP := attname = comma, separated, list, of, accepted, values
108: // example: organisation = foo && module = bar, baz
109: filterExpression = filterExpression.trim();
110: int index = filterExpression.indexOf(AND);
111: if (index == -1) {
112: index = filterExpression.indexOf(OR);
113: if (index == -1) {
114: if (filterExpression.startsWith(NOT)) {
115: return new NotFilter(
116: parseExpression(filterExpression
117: .substring(NOT.length())));
118: } else {
119: index = filterExpression.indexOf("=");
120: if (index == -1) {
121: throw new IllegalArgumentException(
122: "bad filter expression: "
123: + filterExpression
124: + ": no equal sign found");
125: }
126: final String attname = filterExpression.substring(
127: 0, index).trim();
128: String[] values = filterExpression.substring(
129: index + 1).trim().split(",");
130: final Matcher[] matchers = new Matcher[values.length];
131: for (int i = 0; i < values.length; i++) {
132: matchers[i] = matcher.getMatcher(values[i]
133: .trim());
134: }
135: return new Filter() {
136: public boolean accept(Object o) {
137: IvyEvent e = (IvyEvent) o;
138: String val = (String) e.getAttributes()
139: .get(attname);
140: if (val == null) {
141: return false;
142: }
143: for (int i = 0; i < matchers.length; i++) {
144: if (matchers[i].matches(val)) {
145: return true;
146: }
147: }
148: return false;
149: }
150: };
151: }
152: } else {
153: return new OrFilter(parseExpression(filterExpression
154: .substring(0, index)),
155: parseExpression(filterExpression
156: .substring(index + OR.length())));
157: }
158: } else {
159: return new AndFilter(parseExpression(filterExpression
160: .substring(0, index)),
161: parseExpression(filterExpression.substring(index
162: + AND.length())));
163: }
164: }
165:
166: public boolean accept(Object o) {
167: if (!(o instanceof IvyEvent)) {
168: return false;
169: }
170: return nameFilter.accept(o) && attFilter.accept(o);
171: }
172:
173: }
|