001 /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright (c) 2013 Edugility LLC.
004 *
005 * Permission is hereby granted, free of charge, to any person
006 * obtaining a copy of this software and associated documentation
007 * files (the "Software"), to deal in the Software without
008 * restriction, including without limitation the rights to use, copy,
009 * modify, merge, publish, distribute, sublicense and/or sell copies
010 * of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
020 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
021 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
022 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
023 * DEALINGS IN THE SOFTWARE.
024 *
025 * The original copy of this license is available at
026 * http://www.opensource.org/license/mit-license.html.
027 */
028 package com.edugility.objexj;
029
030 import java.io.IOException;
031
032 import java.text.ParseException;
033
034 import java.util.List;
035
036 import com.edugility.objexj.engine.Engine;
037 import com.edugility.objexj.engine.Program;
038
039 import com.edugility.objexj.parser.Parser;
040
041 /**
042 * A regular expression pattern for arbitrary {@link Object}s.
043 *
044 * <p>A {@link Pattern} encapsulates the rules by which {@link List}s
045 * of objects—the <em>input</em>—are matched or rejected.
046 * {@link Pattern}s are compiled from textual representations that
047 * bear a striking and intentional similarity to regular regular
048 * expressions.</p>
049 *
050 * <p>A normal user interaction with this class might consist of code
051 * like the following:</p>
052 *
053 * <blockquote><pre>
054 * final {@link Pattern}<Exception> p = Pattern.{@link #compile(String)
055 * compile}("<a href="../../../../syntax.html"
056 * target="_parent">^javax.persistence.PersistenceException/java.lang.Throwable*</a>");
057 * assert p != null;
058 * final {@link Matcher}<{@link Exception}> matcher = p.{@link #matcher(List) matcher}(listOfExceptions);
059 * assert matcher != null;
060 * // Call matcher.{@link Matcher#lookingAt() lookingAt()} or....</pre></blockquote>
061 *
062 * <p>{@link Pattern}s are not safe for use by multiple Java
063 * {@linkplain java.lang.Thread threads}.</p>
064 *
065 * @param <T> the type of {@link Object} a {@link Pattern} can match
066 *
067 * @author <a href="http://about.me/lairdnelson"
068 * target="_parent">Laird Nelson</a>
069 *
070 * @see #compile(String)
071 *
072 * @see #matcher(List)
073 *
074 * @see Matcher
075 *
076 * @see <a href="../../../../syntax.html" target="_parent">Syntax
077 * Guide</a>
078 */
079 public class Pattern<T> {
080
081 /**
082 * The {@link Engine} to use to {@linkplain Engine#run(Program,
083 * List) run <tt>Program</tt>s}. This field is never {@code null}.
084 */
085 private final Engine<T> engine;
086
087 /**
088 * The {@link Program} this {@link Pattern} will {@linkplain
089 * Engine#run(Program, List) run}. This field is never {@code
090 * null}.
091 */
092 private final Program<T> program;
093
094 /**
095 * Creates a new {@link Pattern} with the supplied {@link Program}.
096 * A new {@link Engine} will be used to {@linkplain
097 * Engine#run(Program, List) run} the supplied {@link Program}.
098 *
099 * @param program the {@link Program} to {@linkplain
100 * Engine#run(Program, List) run}; must not be {@code null}
101 *
102 * @exception IllegalArgumentException if {@code program} is {@code
103 * null}
104 *
105 * @see #Pattern(Engine, Program)
106 *
107 * @see #compile(String)
108 */
109 private Pattern(final Program<T> program) {
110 this(null, program);
111 }
112
113 /**
114 * Creates a new {@link Pattern} with the supplied {@link Engine}
115 * and {@link Program}.
116 *
117 * @param engine the {@link Engine} that will be used to {@linkplain
118 * Engine#run(Program, List) run} the supplied {@link Program}; if
119 * {@code null} a new {@link Engine} will be used instead
120 *
121 * @param program the {@link Program} to {@linkplain
122 * Engine#run(Program, List) run}; must not be {@code null}
123 *
124 * @exception IllegalArgumentException if {@code program} is {@code
125 * null}
126 *
127 * @see #compile(String)
128 */
129 private Pattern(final Engine<T> engine, final Program<T> program) {
130 super();
131 if (program == null) {
132 throw new IllegalArgumentException("program", new NullPointerException("program"));
133 }
134 this.program = program;
135 if (engine == null) {
136 this.engine = new Engine<T>();
137 } else {
138 this.engine = engine;
139 }
140 }
141
142 /**
143 * Returns a {@link Matcher} initialized to match the supplied
144 * {@link List} of items. This method never returns {@code null}.
145 *
146 * @param items the input; may be {@code null}
147 *
148 * @return a new {@link Matcher}; never {@code null}
149 */
150 public final Matcher<T> matcher(final List<? extends T> items) {
151 return new Matcher<T>(this, items);
152 }
153
154 /**
155 * Returns the {@link Engine} that will be used to {@linkplain
156 * Engine#run(Program, List) run} this {@link Pattern}'s {@linkplain
157 * #getProgram() affiliated <tt>Program</tt>}. This method never
158 * returns {@code null}.
159 *
160 * @return a non-{@code null} {@link Engine}
161 */
162 final Engine<T> getEngine() {
163 assert this.engine != null;
164 return this.engine;
165 }
166
167 /**
168 * Returns the {@link Program} that this {@link Pattern} will cause
169 * to be {@linkplain Engine#run(Program, List) run} by {@link
170 * Matcher}s {@linkplain #matcher(List) supplied by its
171 * <tt>matcher(List)</tt> method}. This method never returns {@code
172 * null}.
173 *
174 * @return a non-{@code null} {@link Program}
175 */
176 final Program<T> getProgram() {
177 assert this.program != null;
178 return this.program;
179 }
180
181 /**
182 * Returns a non-{@code null} {@link String} representation of this
183 * {@link Pattern}.
184 *
185 * <p>This implementation attempts to return the original source
186 * code used to produce this {@link Pattern}. If that fails for
187 * some reason, then the normal {@link Object#toString()} method
188 * return value is returned instead.</p>
189 *
190 * @return a non-{@code null} {@link String} representation of this
191 * {@link Pattern}
192 */
193 @Override
194 public String toString() {
195 final Program<?> program = this.getProgram();
196 assert program != null;
197 final String returnValue;
198 final Object source = program.getSource();
199 if (source == null) {
200 returnValue = super.toString();
201 } else {
202 returnValue = source.toString();
203 }
204 return returnValue;
205 }
206
207
208 /*
209 * Static methods.
210 */
211
212
213 /**
214 * Compiles a new {@link Pattern} from the supplied source code.
215 *
216 * @param <T> the type of {@link Object} the resulting {@link
217 * Pattern} will be capable of {@linkplain Pattern#matcher(List)
218 * producing} {@link Matcher}s for
219 *
220 * @param source the source code for the {@link Pattern}; must not
221 * be {@code null}
222 *
223 * @return a new, non-{@code null} {@link Pattern}
224 *
225 * @exception IllegalArgumentException if {@code source} is {@code
226 * null}
227 *
228 * @exception IOException if the source code could not be compiled
229 * because the source code could not be physically read for some
230 * reason
231 *
232 * @exception ParseException if the source code could be read but
233 * was syntactically invalid
234 *
235 * @see <a href="../../../../syntax.html" target="_parent">Syntax
236 * Guide</a>
237 */
238 public static final <T> Pattern<T> compile(final String source) throws IOException, ParseException {
239 if (source == null) {
240 throw new IllegalArgumentException("source", new NullPointerException("source"));
241 }
242 final Program<T> p = new Parser().parse(source);
243 assert p != null;
244 return new Pattern<T>(p);
245 }
246
247 }