001/* An actor that computes a specified String comparison function on 002 the two String inputs. 003 004 Copyright (c) 2003-2014 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.actor.lib.string; 030 031import java.util.Locale; 032 033import ptolemy.actor.TypedAtomicActor; 034import ptolemy.actor.TypedIOPort; 035import ptolemy.actor.parameters.PortParameter; 036import ptolemy.data.BooleanToken; 037import ptolemy.data.StringToken; 038import ptolemy.data.expr.Parameter; 039import ptolemy.data.type.BaseType; 040import ptolemy.kernel.CompositeEntity; 041import ptolemy.kernel.util.Attribute; 042import ptolemy.kernel.util.IllegalActionException; 043import ptolemy.kernel.util.InternalErrorException; 044import ptolemy.kernel.util.NameDuplicationException; 045 046/////////////////////////////////////////////////////////////////// 047//// StringCompare 048 049/** 050 Compare two strings specified either as inputs or parameters. The output 051 is either true or false, depending on whether the comparison function is 052 satisfied. 053 054 <p> The comparison functions are:</p> 055 <ul> 056 <li> <b>equals</b>: Output true if the strings are equal (Default).</li> 057 <li> <b>startsWith</b>: Output true if <i>firstString</i> starts with 058 <i>secondString</i>.</li> 059 <li> <b>endsWith</b>: Output true if <i>firstString</i> ends with 060 <i>secondString</i>.</li> 061 <li> <b>contains</b>: Output true if <i>firstString</i> contains 062 <i>secondString</i>.</li> 063 </ul> 064 <p>The strings to be compared will be taken from the inputs if they are 065 available, and otherwise will be taken from the corresponding parameters.</p> 066 067 @author Vinay Krishnan, Daniel Lázaro Cuadrado (contributor: Edward A. Lee) 068 @version $Id$ 069 @since Ptolemy II 4.0 070 @Pt.ProposedRating Green (kapokasa) 071 @Pt.AcceptedRating Green (net) 072 */ 073public class StringCompare extends TypedAtomicActor { 074 /** Construct an actor with the given container and name. 075 * Construct the two operand input PortParameters (initialized to "") 076 * and the output port which outputs the result of the various comparison 077 * functions executed by the actor. The function to be executed is 078 * decided by the parameter <i>function</i>, which is also initialized 079 * here to the comparison function equals. The <i>ignoreCase</i> parameter 080 * allows to ignore case when comparing. 081 * @param container The container. 082 * @param name The name of this actor. 083 * @exception IllegalActionException If the actor cannot be contained 084 * by the proposed container. 085 * @exception NameDuplicationException If the container already has an 086 * actor with this name. 087 */ 088 public StringCompare(CompositeEntity container, String name) 089 throws NameDuplicationException, IllegalActionException { 090 super(container, name); 091 092 // Parameters 093 function = new Parameter(this, "function"); 094 function.setStringMode(true); 095 function.setExpression("equals"); 096 function.addChoice("equals"); 097 function.addChoice("startsWith"); 098 function.addChoice("endsWith"); 099 function.addChoice("contains"); 100 _function = _EQUALS; 101 102 ignoreCase = new Parameter(this, "ignoreCase"); 103 ignoreCase.setTypeEquals(BaseType.BOOLEAN); 104 ignoreCase.setToken(new BooleanToken(false)); 105 106 // Ports 107 firstString = new PortParameter(this, "firstString"); 108 firstString.setExpression(""); 109 firstString.setStringMode(true); 110 111 secondString = new PortParameter(this, "secondString"); 112 secondString.setExpression(""); 113 secondString.setStringMode(true); 114 115 output = new TypedIOPort(this, "output"); 116 output.setOutput(true); 117 output.setTypeEquals(BaseType.BOOLEAN); 118 119 _attachText("_iconDescription", 120 "<svg>\n" + "<rect x=\"-30\" y=\"-15\" " 121 + "width=\"60\" height=\"30\" " 122 + "style=\"fill:white\"/>\n" + "</svg>\n"); 123 } 124 125 /////////////////////////////////////////////////////////////////// 126 //// ports and parameters //// 127 128 /** The comparison function to be performed. The choices are: 129 * <ul> 130 * <li> <b>equals</b>: Compares firstString to another secondString 131 * (Default). 132 * <li> <b>startsWith</b>: Tests whether firstString starts with 133 * secondString. 134 * <li> <b>endsWith</b>: Tests whether firstString ends with secondString. 135 * <li> <b>contains</b>: Tests whether firstString contains secondString. 136 * </ul> 137 */ 138 public Parameter function; 139 140 /** The input PortParameter for the first string of type string. 141 */ 142 public PortParameter firstString; 143 144 /** The parameter to state whether to ignore case. This is a 145 * boolean that defaults to false. 146 */ 147 public Parameter ignoreCase; 148 149 /** The output port for the result of type BooleanToken. 150 */ 151 public TypedIOPort output; 152 153 /** The input PortParameter for the second string of type string. 154 */ 155 public PortParameter secondString; 156 157 /////////////////////////////////////////////////////////////////// 158 //// public methods //// 159 160 /** Override the base class to determine which function is being 161 * specified. 162 * @param attribute The attribute that changed. 163 * @exception IllegalActionException If the function is not recognized. 164 */ 165 @Override 166 public void attributeChanged(Attribute attribute) 167 throws IllegalActionException { 168 if (attribute == function) { 169 // Use getToken() not getExpression() so that substitutions happen. 170 String functionName = ((StringToken) function.getToken()) 171 .stringValue(); 172 173 if (functionName.equals("equals")) { 174 _function = _EQUALS; 175 } else if (functionName.equals("startsWith")) { 176 _function = _STARTSWITH; 177 } else if (functionName.equals("endsWith")) { 178 _function = _ENDSWITH; 179 } else if (functionName.equals("contains")) { 180 _function = _CONTAINS; 181 } else { 182 throw new IllegalActionException(this, 183 "Unrecognized string function: " + functionName); 184 } 185 } else { 186 super.attributeChanged(attribute); 187 } 188 } 189 190 /** Consume exactly one input token from each input port, and compute the 191 * specified string function of the input taking into account the 192 * <i>ignoreCase</i> parameter. 193 * @exception IllegalActionException If there is no director. 194 */ 195 @Override 196 public void fire() throws IllegalActionException { 197 super.fire(); 198 199 // They are port parameters and they need to be updated. 200 firstString.update(); 201 secondString.update(); 202 203 String input1 = ((StringToken) firstString.getToken()).stringValue(); 204 String input2 = ((StringToken) secondString.getToken()).stringValue(); 205 206 if (((BooleanToken) ignoreCase.getToken()).booleanValue()) { 207 input1 = input1.toLowerCase(Locale.getDefault()); 208 input2 = input2.toLowerCase(Locale.getDefault()); 209 } 210 211 output.send(0, new BooleanToken(_doFunction(input1, input2))); 212 } 213 214 /////////////////////////////////////////////////////////////////// 215 //// private methods //// 216 217 /** Calculate the function selected on the given inputs. 218 * @param input1 The first input String. 219 * @param input2 The second input String. 220 * @return The result of applying the function. 221 */ 222 private boolean _doFunction(String input1, String input2) { 223 boolean result; 224 225 switch (_function) { 226 case _EQUALS: 227 result = input1.equals(input2); 228 break; 229 230 case _STARTSWITH: 231 result = input1.startsWith(input2); 232 break; 233 234 case _ENDSWITH: 235 result = input1.endsWith(input2); 236 break; 237 238 case _CONTAINS: 239 result = input1.indexOf(input2) >= 0; 240 break; 241 242 default: 243 throw new InternalErrorException( 244 "Invalid value for _function private variable. " 245 + "StringCompare actor (" + getFullName() + ")" 246 + " on function type " + _function); 247 } 248 249 return result; 250 } 251 252 /////////////////////////////////////////////////////////////////// 253 //// private variables //// 254 // An indicator for the function to compute. 255 private int _function; 256 257 // Constants used for more efficient execution. 258 private static final int _EQUALS = 0; 259 260 private static final int _STARTSWITH = 1; 261 262 private static final int _ENDSWITH = 2; 263 264 private static final int _CONTAINS = 3; 265}