001/* Pattern match a string to a regular expression.
002
003 Copyright (c) 2003-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib.string;
029
030import java.util.regex.Matcher;
031import java.util.regex.Pattern;
032import java.util.regex.PatternSyntaxException;
033
034import ptolemy.actor.TypedAtomicActor;
035import ptolemy.actor.TypedIOPort;
036import ptolemy.actor.parameters.PortParameter;
037import ptolemy.data.BooleanToken;
038import ptolemy.data.StringToken;
039import ptolemy.data.expr.SingletonParameter;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.Attribute;
043import ptolemy.kernel.util.IllegalActionException;
044import ptolemy.kernel.util.InternalErrorException;
045import ptolemy.kernel.util.NameDuplicationException;
046import ptolemy.kernel.util.Workspace;
047
048///////////////////////////////////////////////////////////////////
049//// StringMatches
050
051/**
052 Pattern match a string to a regular expression and output a true if it
053 matches and a false if it does not. For a reference on regular
054 expression syntax see:
055 <a href="http://download.oracle.com/javase/tutorial/essential/regex/#in_browser">
056 http://download.oracle.com/javase/tutorial/essential/regex/</a>.
057
058 @author Antonio Yordan-Nones, Colin Cochran, Contributor: Edward A. Lee
059 @version $Id$
060 @since Ptolemy II 4.0
061 @Pt.ProposedRating Green (djstone)
062 @Pt.AcceptedRating Green (eal)
063 */
064public class StringMatches extends TypedAtomicActor {
065    /** Construct an actor with the given container and name.
066     *  In addition to invoking the base class constructors, this constructs
067     *  the <i>pattern</i> and <i>matchString</i> PortParameters,
068     *  and the <i>output</i> port.
069     *  @param container The container.
070     *  @param name The name of this actor.
071     *  @exception IllegalActionException If the actor cannot be contained
072     *   by the proposed container.
073     *  @exception NameDuplicationException If the container already has an
074     *   actor with this name.
075     */
076    public StringMatches(CompositeEntity container, String name)
077            throws NameDuplicationException, IllegalActionException {
078        super(container, name);
079
080        // Create one matchString portParameter, one matchString port,
081        // and one output port.
082        pattern = new PortParameter(this, "pattern");
083        pattern.setStringMode(true);
084        pattern.setExpression("");
085        new SingletonParameter(pattern.getPort(), "_showName")
086                .setToken(BooleanToken.TRUE);
087
088        matchString = new PortParameter(this, "matchString");
089        matchString.setStringMode(true);
090        matchString.setExpression("");
091        new SingletonParameter(matchString.getPort(), "_showName")
092                .setToken(BooleanToken.TRUE);
093
094        output = new TypedIOPort(this, "output", false, true);
095        output.setTypeEquals(BaseType.BOOLEAN);
096    }
097
098    ///////////////////////////////////////////////////////////////////
099    ////                     ports and parameters                  ////
100
101    /** The string to be pattern matched to the regular expression.
102     */
103    public PortParameter matchString;
104
105    /** Output is true if the pattern exists in the string, false otherwise.
106     *  Its type is boolean.
107     */
108    public TypedIOPort output;
109
110    /** The regular expression to be pattern matched with the
111     *  matchString string.
112     *  Its default parameter is an empty string that matches no strings.
113     */
114    public PortParameter pattern;
115
116    ///////////////////////////////////////////////////////////////////
117    ////                         public methods                    ////
118
119    /** Override the base class to compile the regular expression if
120     *  it has changed.
121     *  @param attribute The attribute that changed.
122     *  @exception IllegalActionException If the pattern cannot be compiled
123     *  into a regular expression.
124     */
125    @Override
126    public void attributeChanged(Attribute attribute)
127            throws IllegalActionException {
128        if (attribute == pattern) {
129            try {
130                String patternValue = ((StringToken) pattern.getToken())
131                        .stringValue();
132                _pattern = Pattern.compile(patternValue);
133            } catch (PatternSyntaxException ex) {
134                String patternValue = ((StringToken) pattern.getToken())
135                        .stringValue();
136                throw new IllegalActionException(this, ex,
137                        "Failed to compile regular expression \"" + patternValue
138                                + "\"");
139            }
140        } else {
141            super.attributeChanged(attribute);
142        }
143    }
144
145    /** Clone the actor into the specified workspace. This calls the
146     *  base class and initializes private variables.
147     *  @param workspace The workspace for the new object.
148     *  @return A new actor.
149     *  @exception CloneNotSupportedException If a derived class has
150     *   an attribute that cannot be cloned.
151     */
152    @Override
153    public Object clone(Workspace workspace) throws CloneNotSupportedException {
154        StringMatches newObject = (StringMatches) super.clone(workspace);
155        String patternValue = null;
156        try {
157            patternValue = ((StringToken) newObject.pattern.getToken())
158                    .stringValue();
159            newObject._pattern = Pattern.compile(patternValue);
160        } catch (Exception ex) {
161            throw new InternalErrorException(this, ex,
162                    "Failed to compile regular expression \"" + patternValue
163                            + "\"");
164        }
165        return newObject;
166    }
167
168    /** Pattern match a regular expression against a supplied
169     *  matchString and output a true if they match and a false
170     *  otherwise.
171     *  @exception IllegalActionException If there is no director.
172     */
173    @Override
174    public void fire() throws IllegalActionException {
175        super.fire();
176        pattern.update();
177        matchString.update();
178
179        String matchStringValue = ((StringToken) matchString.getToken())
180                .stringValue();
181        Matcher match = _pattern.matcher(matchStringValue);
182        output.send(0, new BooleanToken(match.find()));
183    }
184
185    ///////////////////////////////////////////////////////////////////
186    ////                         private variables                 ////
187    // The pattern for the regular expression.
188    private Pattern _pattern;
189}