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&mdash;the <em>input</em>&mdash;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    }