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 }