001/* A publisher that transparently tunnels messages to subscribers and saves its output for testing
002
003 Copyright (c) 2007-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;
029
030import java.util.ArrayList;
031
032import ptolemy.data.ArrayToken;
033import ptolemy.data.BooleanToken;
034import ptolemy.data.RecordToken;
035import ptolemy.data.Token;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NameDuplicationException;
039
040//////////////////////////////////////////////////////////////////////////
041//// PublisherTest
042
043/**
044 This actor publishes input tokens on a named channel and compares
045 the inputs against the value specified by the <i>correctValues</i> parameter.
046
047 <p>This actor combines the {@link ptolemy.actor.lib.Publisher} actor
048 and the {@link ptolemy.actor.lib.Test} actor.  Thus, it has quite
049 a bit of duplicated code from the Test actor.
050
051 @author Christopher Brooks, based on Test, which has Edward A. Lee and Jim Armstrong as authors
052 @version $Id$
053 @since Ptolemy II 6.1
054 @Pt.ProposedRating Red (cxh)
055 @Pt.AcceptedRating Red (cxh)
056 */
057public class PublisherTest extends PublisherNonStrictTest {
058
059    /** Construct a publisher with the specified container and name.
060     *  @param container The container actor.
061     *  @param name The name of the actor.
062     *  @exception IllegalActionException If the actor is not of an acceptable
063     *   class for the container.
064     *  @exception NameDuplicationException If the name coincides with
065     *   an actor already in the container.
066     */
067    public PublisherTest(CompositeEntity container, String name)
068            throws IllegalActionException, NameDuplicationException {
069        super(container, name);
070
071        // Note that the parent class (PublisherNonStrictTest) does
072        // not have a multiport input port.
073        input.setMultiport(true);
074    }
075
076    /** Read at most one input token from each
077     *  input channel and send it to the subscribers,
078     *  if any.
079     *  Read one token from each input channel and compare against
080     *  the value specified in <i>correctValues</i>. If the value
081     *  matches, then output false (to indicate that the test is not
082     *  complete yet) and return.  Otherwise, throw an exception.
083     *  If the iteration count is larger than the length of
084     *  <i>correctValues</i>, then output <i>true</i> and return,
085     *  indicating that the test is complete, i.e. that all
086     *  values in <i>correctValues</i> have been matched.
087     *  @exception IllegalActionException If there is no director.
088     */
089    @Override
090    public void fire() throws IllegalActionException {
091        super.fire();
092        int width = input.getWidth();
093
094        // If we are in training mode, read the inputs and add to the
095        // training data.
096        boolean training = ((BooleanToken) trainingMode.getToken())
097                .booleanValue();
098
099        if (training) {
100            if (_trainingTokens == null) {
101                _trainingTokens = new ArrayList();
102            }
103
104            if (_debugging) {
105                _debug("PublisherTest: width: " + width + " "
106                        + input.hasToken(0));
107            }
108            if (width == 1) {
109                if (input.hasToken(0)) {
110                    Token token = input.get(0);
111                    output.send(0, token);
112                    if (token instanceof ArrayToken) {
113                        Token[] innerArrayToken = new Token[1];
114                        innerArrayToken[0] = token;
115                        _trainingTokens.add(innerArrayToken);
116                    } else {
117                        _trainingTokens.add(token);
118                    }
119                }
120            } else {
121                ArrayList arrayList = new ArrayList();
122
123                for (int i = 0; i < width; i++) {
124                    if (input.hasToken(i)) {
125                        Token token = input.get(i);
126                        arrayList.add(token);
127                        output.send(i, token);
128                    }
129                }
130
131                _trainingTokens.add(arrayList);
132            }
133
134            return;
135        }
136
137        if (_numberOfInputTokensSeen >= ((ArrayToken) correctValues.getToken())
138                .length()) {
139            // Consume and discard input values.  We are beyond the end
140            // of the correctValues array.
141            for (int i = 0; i < width; i++) {
142                if (input.hasToken(i)) {
143                    Token token = input.get(i);
144                    output.send(i, token);
145                }
146            }
147
148            // Indicate that the test has passed.
149            output.send(0, new BooleanToken(true));
150            return;
151        }
152
153        Token referenceToken = ((ArrayToken) correctValues.getToken())
154                .getElement(_numberOfInputTokensSeen);
155        Token[] reference;
156
157        if (width == 1 && !(referenceToken instanceof ArrayToken)) {
158            reference = new Token[1];
159            reference[0] = referenceToken;
160        } else {
161            try {
162                reference = ((ArrayToken) referenceToken).arrayValue();
163            } catch (ClassCastException ex) {
164                throw new IllegalActionException(this,
165                        "Test fails in iteration " + _numberOfInputTokensSeen
166                                + ".\n" + "Width of input is " + width
167                                + ", but correctValues parameter "
168                                + "is not an array " + "of arrays.");
169            }
170
171            if (width != reference.length) {
172                throw new IllegalActionException(this,
173                        "Test fails in iteration " + _numberOfInputTokensSeen
174                                + ".\n" + "Width of input is " + width
175                                + ", which does not match "
176                                + "the  width of the "
177                                + _numberOfInputTokensSeen + "-th element of"
178                                + " correctValues, " + reference.length);
179            }
180        }
181
182        for (int i = 0; i < width; i++) {
183            if (!input.hasToken(i)) {
184                throw new IllegalActionException(this,
185                        "Test fails in iteration " + _numberOfInputTokensSeen
186                                + ".\n" + "Empty input on channel " + i);
187            }
188
189            Token token = input.get(i);
190
191            boolean isClose;
192
193            try {
194                isClose = token.isCloseTo(reference[i], _tolerance)
195                        .booleanValue()
196                        || token.isNil() && reference[i].isNil();
197                // Additional guards makes things slightly easier for
198                // Copernicus.
199                if (token instanceof ArrayToken
200                        && reference[i] instanceof ArrayToken) {
201                    isClose |= NonStrictTest._isCloseToIfNilArrayElement(token,
202                            reference[i], _tolerance);
203                }
204                if (token instanceof RecordToken
205                        && reference[i] instanceof RecordToken) {
206                    isClose |= NonStrictTest._isCloseToIfNilRecordElement(token,
207                            reference[i], _tolerance);
208                }
209
210            } catch (IllegalActionException ex) {
211                // Chain the exceptions together so we know which test
212                // actor failed if there was more than one...
213                throw new IllegalActionException(this, ex,
214                        "Test fails in iteration " + _numberOfInputTokensSeen
215                                + ".\n" + "Value was: " + token
216                                + ". Should have been: " + reference[i]);
217            }
218
219            if (!isClose) {
220                throw new IllegalActionException(this,
221                        "Test fails in iteration " + _numberOfInputTokensSeen
222                                + ".\n" + "Value was: " + token
223                                + ". Should have been: " + reference[i]);
224            }
225            output.send(i, token);
226        }
227
228        _numberOfInputTokensSeen++;
229    }
230
231    /** Override the base class to do nothing and return true.
232     *  @return True.
233     */
234    @Override
235    public boolean postfire() {
236        return true;
237    }
238}