001/* An attribute that is used for invoking Chic from inside a ptolemy model.
002
003 Copyright (c) 1998-2016 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.chic;
029
030import java.util.Iterator;
031import java.util.LinkedList;
032import java.util.List;
033
034import chic.ChicForPtolemy;
035import ptolemy.actor.Actor;
036import ptolemy.actor.CompositeActor;
037import ptolemy.actor.IOPort;
038import ptolemy.data.BooleanToken;
039import ptolemy.data.expr.SingletonParameter;
040import ptolemy.kernel.ComponentEntity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import ptolemy.kernel.util.NamedObj;
045import ptolemy.kernel.util.Settable;
046import ptolemy.kernel.util.StringAttribute;
047
048///////////////////////////////////////////////////////////////////
049//// ChicInvoker
050
051/**
052 This attribute is a visible attribute used for invoking Chic (Checker
053 for Interface Compatibility) on its container. Chic is invoked by
054 right clicking on the attribute and selecting one of the options:
055 "CHIC: Asynchronous I/O" and "CHIC: Synchronous A/G".
056 The expression of the InterfaceName attribute specifies the name of the
057 ChicAttribute that is going to be used upon invocation of Chic.
058 Note that every directly or indirectly contained port of the attribute's
059 container must have a name syntactically equivalent to a Java identifier.
060
061 @author Eleftherios Matsikoudis
062 @version $Id$
063 @since Ptolemy II 3.0
064 @Pt.ProposedRating Red (cxh)
065 @Pt.AcceptedRating Red (cxh)
066 */
067public class ChicInvoker extends Attribute {
068    /** Construct an attribute with the specified container and name.
069     *  @param container The container.
070     *  @param name The name of the attribute.
071     *  @exception IllegalActionException If the factory is not of an
072     *   acceptable attribute for the container.
073     *  @exception NameDuplicationException If the name coincides with
074     *   an attribute already in the container.
075     */
076    public ChicInvoker(NamedObj container, String name)
077            throws IllegalActionException, NameDuplicationException {
078        super(container, name);
079
080        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-50\" y=\"-25\" "
081                + "width=\"100\" height=\"50\" " + "style=\"fill:white\"/>\n"
082                + "<image x=\"-50\" y=\"-25\" width=\"100\" height=\"50\" "
083                + "xlink:href=\"ptolemy/chic/chic.gif\"/>\n" + "</svg>\n");
084
085        new ChicControllerFactory(this, "_controllerFactory");
086
087        SingletonParameter hide = new SingletonParameter(this, "_hideName");
088        hide.setToken(BooleanToken.TRUE);
089        hide.setVisibility(Settable.EXPERT);
090
091        chicAttributeName = new StringAttribute(this, "InterfaceName");
092    }
093
094    ///////////////////////////////////////////////////////////////////
095    ////                         parameters                        ////
096
097    /** A StringAttribute representing the name of the ChicAttributes
098     *  that are to be queried while collecting the interfaces upon which
099     *  Chic is to be invoked.
100     */
101    public StringAttribute chicAttributeName;
102
103    ///////////////////////////////////////////////////////////////////
104    ////                         public methods                    ////
105
106    /** Check the interface compatibility of all entities that are directly
107     *  or indirectly contained by the container of this attribute.
108     *  @param compiler The compiler of Chic to use.
109     *  @see #ASYNCHRONOUS_IO
110     *  @see #SYNCHRONOUS_AG
111     *  @param silent True if the interface compatibility checking is to be
112     *   carried out without invoking the user interface of Chic.
113     *  @return True if the interfaces of the contained entities are
114     *   compatible.
115     *  @exception IllegalActionException If a port contained directly or
116     *   indirectly by the <i>model</i> has width greater than one or is
117     *   both an input and output port.
118     *  @exception NameDuplicationException If the container of the attribute
119     *   or one of the contained entities already contains an attribute of
120     *   class other than ChicAttribute with the same name as the expression
121     *   of the InterfaceName.
122     */
123    public boolean checkInterfaceCompatibility(int compiler, boolean silent)
124            throws IllegalActionException, NameDuplicationException {
125        return _checkInterfaceCompatibility(
126                (CompositeActor) this.getContainer(), compiler, silent,
127                chicAttributeName.getExpression());
128    }
129
130    ///////////////////////////////////////////////////////////////////
131    ////                         public variables                  ////
132
133    /** Indicate use of the Asynchronous I/O compiler.
134     */
135    public static final int ASYNCHRONOUS_IO = ChicForPtolemy.ASYN;
136
137    //    /** Indicate use of the Bidirectional Syn A/G compiler
138    //     */
139    //    public static final int BIDIRECTIONAL_SYN_AG = ChicForPtolemy.BISYNAG;
140    //    /** Indiate use of the Stateful Software compiler
141    //     */
142    //    public static final int STATEFUL_SOFTWARE = ChicForPtolemy.SOFT_STATEFUL;
143    //    /** Indicate use of the Stateless Software compiler
144    //     */
145    //    public static final int STATELESS_SOFTWARE = ChicForPtolemy.SOFT_STATELESS;
146
147    /** Indicate use of the Synchronous A/G compiler.
148     */
149    public static final int SYNCHRONOUS_AG = ChicForPtolemy.SYNAG;
150
151    ///////////////////////////////////////////////////////////////////
152    ////                         protected methods                 ////
153
154    /** Check the interface compatibility of all entities that are directly
155     *  or indirectly contained by the <i>model</i>.
156     *  @param model The model to check interface compatibility.
157     *  @param compiler The compiler of Chic to use.
158     *  @see #ASYNCHRONOUS_IO
159     *  @see #SYNCHRONOUS_AG
160     *  @param silent True if the interface compatibility checking is to be
161     *   carried out without invoking the user interface of Chic.
162     *  @param name The name of the ChicAttribute.
163     *  @return True if the interfaces of the contained entities are
164     *   compatible.
165     *  @exception IllegalActionException If a port contained directly or
166     *   indirectly by the <i>model</i> has width greater than one or is
167     *   both an input and output port.
168     *  @exception NameDuplicationException If the <i>model</i> or one of the
169     *   contained entities already contains an attribute of class other than
170     *   ChicAttribute with the same name as <i>name</i>.
171     */
172    protected boolean _checkInterfaceCompatibility(CompositeActor model,
173            int compiler, boolean silent, String name)
174            throws IllegalActionException, NameDuplicationException {
175        ChicAttribute chicAttribute;
176        String chicInterface;
177        StringBuffer collectedInterfaces = new StringBuffer("");
178
179        try {
180            _workspace.getReadAccess();
181
182            Iterator entities = model.entityList().iterator();
183
184            // Collect the interfaces from all directly contained entities.
185            while (entities.hasNext()) {
186                ComponentEntity entity = (ComponentEntity) entities.next();
187
188                // Get the interface of the entity.
189                if (entity.isAtomic()) {
190                    chicAttribute = (ChicAttribute) entity.getAttribute(name);
191                } else {
192                    if (_checkInterfaceCompatibility((CompositeActor) entity,
193                            compiler, true, name)) {
194                        chicAttribute = (ChicAttribute) entity
195                                .getAttribute(name);
196                    } else {
197                        return false;
198                    }
199                }
200
201                // If the entity does not have a declared interface skip it.
202                if (chicAttribute == null) {
203                    continue;
204                }
205
206                chicInterface = chicAttribute.getExpression();
207
208                // Parse and refactor it.
209                // Construct an iterator for the list of opaque ports
210                // interfacing the entity.
211                Iterator ports;
212
213                if (entity.isOpaque()) {
214                    ports = entity.portList().iterator();
215                } else {
216                    List insidePortList = new LinkedList();
217                    Iterator transparentPorts = entity.portList().iterator();
218
219                    while (transparentPorts.hasNext()) {
220                        IOPort transparentPort = (IOPort) transparentPorts
221                                .next();
222                        insidePortList
223                                .addAll(transparentPort.deepInsidePortList());
224                    }
225
226                    ports = insidePortList.iterator();
227                }
228
229                // Refactor the interface according to its type.
230                switch (compiler) {
231                case ASYNCHRONOUS_IO:
232
233                    while (ports.hasNext()) {
234                        IOPort port = (IOPort) ports.next();
235
236                        // Currently ports with width greater than one are not supported.
237                        if (port.getWidth() > 1) {
238                            throw new IllegalActionException("Ports with "
239                                    + "width greater than one are not "
240                                    + "supported in the current "
241                                    + "implementation.");
242                        }
243
244                        // Replace port_name.remote.action with
245                        // remote_port_ull_name.action
246                        if (entity.deepContains(port)) {
247                            Iterator connectedPorts = port
248                                    .deepConnectedPortList().iterator();
249
250                            while (connectedPorts.hasNext()) {
251                                IOPort connectedPort = (IOPort) connectedPorts
252                                        .next();
253
254                                if (entity.isOpaque()) {
255                                    if (model.deepContains(connectedPort)) {
256                                        chicInterface = chicInterface
257                                                .replaceAll("(?<!(\\w|\\$|\\.))"
258                                                        + port.getName()
259                                                        + "\\.remote(?=\\.(\\w|\\$)+(\\s|$))",
260                                                        (Actor) connectedPort
261                                                                .getContainer() == model
262                                                                        ? _sanitizeName(
263                                                                                connectedPort
264                                                                                        .getName()
265                                                                                        + ".inside")
266                                                                        : _sanitizeName(
267                                                                                connectedPort
268                                                                                        .getFullName()));
269                                    } else {
270                                        chicInterface = chicInterface
271                                                .replaceAll("(?<!(\\w|\\$|\\.))"
272                                                        + port.getName()
273                                                        + "\\.remote(?=\\.(\\w|\\$)+(\\s|$))",
274                                                        _sanitizeName(port
275                                                                .getFullName()));
276                                    }
277                                } else {
278                                    if (model.deepContains(connectedPort)) {
279                                        chicInterface = chicInterface
280                                                .replaceAll("(?<!(\\w|\\$|\\.))"
281                                                        + port.getFullName()
282                                                        + "\\.remote(?=\\.(\\w|\\$)+(\\s|$))",
283                                                        (Actor) connectedPort
284                                                                .getContainer() == model
285                                                                        ? _sanitizeName(
286                                                                                connectedPort
287                                                                                        .getName()
288                                                                                        + ".inside")
289                                                                        : _sanitizeName(
290                                                                                connectedPort
291                                                                                        .getFullName()));
292                                    }
293                                }
294                            }
295
296                            // Replace port_name.action with port_full_name.action
297                            chicInterface = chicInterface.replaceAll(
298                                    "(?<!(\\w|\\$|\\.))" + port.getName()
299                                            + "(?=\\.(\\w|\\$)+(\\s|$))",
300                                    _sanitizeName(port.getFullName()));
301                        }
302                    }
303
304                    break;
305
306                case SYNCHRONOUS_AG:
307
308                    while (ports.hasNext()) {
309                        IOPort port = (IOPort) ports.next();
310
311                        // Currently ports with width greater than one and
312                        // ports that are both input and output ports are
313                        // not supported.
314                        if (port.isInput() && port.isOutput()
315                                || port.getWidth() > 1) {
316                            throw new IllegalActionException(
317                                    "Ports with width greater than one or "
318                                            + "that are both input and output are not "
319                                            + "supported in the current "
320                                            + "implementation.");
321                        }
322
323                        if (entity.deepContains(port)) {
324                            if (entity.isOpaque()) {
325                                if (port.isInput()) {
326                                    Iterator sourcePorts = port.sourcePortList()
327                                            .iterator();
328
329                                    if (sourcePorts.hasNext()) {
330                                        // Since its width is less than one
331                                        // there is only one port on the
332                                        // outside.
333                                        IOPort sourcePort = (IOPort) sourcePorts
334                                                .next();
335
336                                        if (model.deepContains(sourcePort)) {
337                                            chicInterface = chicInterface
338                                                    .replaceAll(
339                                                            "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
340                                                                    + port.getName()
341                                                                    + "(?!(\\w|\\$|\\.))",
342                                                            (Actor) sourcePort
343                                                                    .getContainer() == model
344                                                                            ? _sanitizeName(
345                                                                                    sourcePort
346                                                                                            .getName()
347                                                                                            + ".inside")
348                                                                            : _sanitizeName(
349                                                                                    sourcePort
350                                                                                            .getFullName()));
351                                        } else {
352                                            chicInterface = chicInterface
353                                                    .replaceAll(
354                                                            "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
355                                                                    + port.getName()
356                                                                    + "(?!(\\w|\\$|\\.))",
357                                                            _sanitizeName(port
358                                                                    .getFullName()));
359                                        }
360                                    } else {
361                                        chicInterface = chicInterface
362                                                .replaceAll(
363                                                        "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
364                                                                + port.getName()
365                                                                + "(?!(\\w|\\$|\\.))",
366                                                        _sanitizeName(port
367                                                                .getFullName()));
368                                    }
369                                } else {
370                                    chicInterface = chicInterface.replaceAll(
371                                            "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
372                                                    + port.getName()
373                                                    + "(?!(\\w|\\$|\\.))",
374                                            _sanitizeName(port.getFullName()));
375                                }
376                            } else {
377                                if (port.isInput()) {
378                                    Iterator sourcePorts = port.sourcePortList()
379                                            .iterator();
380
381                                    if (sourcePorts.hasNext()) {
382                                        // Since its width is less than one
383                                        // there is only one port on the
384                                        // outside.
385                                        IOPort sourcePort = (IOPort) sourcePorts
386                                                .next();
387
388                                        if (model.deepContains(sourcePort)) {
389                                            chicInterface = chicInterface
390                                                    .replaceAll(
391                                                            "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
392                                                                    + port.getFullName()
393                                                                    + "(?!(\\w|\\$|\\.))",
394                                                            (Actor) sourcePort
395                                                                    .getContainer() == model
396                                                                            ? _sanitizeName(
397                                                                                    sourcePort
398                                                                                            .getName()
399                                                                                            + ".inside")
400                                                                            : _sanitizeName(
401                                                                                    sourcePort
402                                                                                            .getFullName()));
403                                        }
404                                    }
405                                }
406                            }
407                        }
408                    }
409
410                    break;
411
412                default:
413                    throw new IllegalActionException(
414                            "Only \"Asynchronous I/O\" and \"Synchronous A/G\" "
415                                    + "are supported in current implementation.");
416                }
417
418                // Append the refactored interface to the collected interfaces.
419                collectedInterfaces.append(chicInterface + "\n");
420            }
421
422            // Collect the interfaces from all directly contained opaque ports.
423            if (model.isOpaque()) {
424                Iterator ports = model.portList().iterator();
425
426                while (ports.hasNext()) {
427                    IOPort port = (IOPort) ports.next();
428
429                    // Get the interface of the port.
430                    chicAttribute = (ChicAttribute) port.getAttribute(name);
431
432                    // If the port does not have a declared interface skip it.
433                    if (chicAttribute == null) {
434                        continue;
435                    }
436
437                    chicInterface = chicAttribute.getExpression();
438
439                    // Parse and refactor it.
440                    switch (compiler) {
441                    case ASYNCHRONOUS_IO:
442
443                        // Currently ports with width greater than one are not supported.
444                        if (port.getWidth() > 1) {
445                            throw new IllegalActionException("Ports with "
446                                    + "width greater than one are not "
447                                    + "supported in the current "
448                                    + "implementation.");
449                        } {
450                        Iterator insidePorts = port.deepInsidePortList()
451                                .iterator();
452
453                        while (insidePorts.hasNext()) {
454                            IOPort insidePort = (IOPort) insidePorts.next();
455                            chicInterface = chicInterface.replaceAll(
456                                    "(?<!(\\w|\\$|\\.))" + port.getName()
457                                            + "\\.inside\\.remote(?=\\.(\\w|\\$)+(\\s|$))",
458                                    (Actor) insidePort.getContainer() == model
459                                            ? _sanitizeName(insidePort.getName()
460                                                    + ".inside")
461                                            : _sanitizeName(
462                                                    insidePort.getFullName()));
463                        }
464                    }
465
466                        break;
467
468                    case SYNCHRONOUS_AG:
469
470                        // Currently ports with width greater than one and
471                        // ports that are both input and output ports are
472                        // not supported.
473                        if (port.isInput() && port.isOutput()
474                                || port.getWidthInside() > 1) {
475                            throw new IllegalActionException(
476                                    "Ports with width greater than one or "
477                                            + "that are both input and output are not "
478                                            + "supported in the current "
479                                            + "implementation.");
480                        }
481
482                        if (port.isOutput()) {
483                            Iterator insidePorts = port.deepInsidePortList()
484                                    .iterator();
485
486                            if (insidePorts.hasNext()) {
487                                // Since it is not a multiport or an input port,
488                                // there is only one port on the inside.
489                                IOPort sourcePort = (IOPort) insidePorts.next();
490                                chicInterface = chicInterface.replaceAll(
491                                        "(?<!(^|[\\n\\r]|\\w|\\$|\\.))"
492                                                + port.getName() + ".inside"
493                                                + "(?!(\\w|\\$|\\.))",
494                                        (Actor) sourcePort
495                                                .getContainer() == model
496                                                        ? _sanitizeName(
497                                                                sourcePort
498                                                                        .getName()
499                                                                        + ".inside")
500                                                        : _sanitizeName(
501                                                                sourcePort
502                                                                        .getFullName()));
503                            }
504                        }
505
506                        break;
507
508                    default:
509                        throw new IllegalActionException(
510                                "Only \"Asynchronous I/O\" and \"Synchronous A/G\" "
511                                        + "are supported in current implementation.");
512                    }
513
514                    // Append the refactored interface to the collected
515                    // interfaces.
516                    collectedInterfaces.append(chicInterface + "\n");
517                }
518            }
519        } finally {
520            _workspace.doneReading();
521        }
522
523        if (!(chicInterface = new String(collectedInterfaces)).equals("")) {
524            System.out.println(chicInterface + "\n");
525
526            ChicForPtolemy chic = new ChicForPtolemy(
527                    new String(collectedInterfaces), compiler, !silent);
528
529            if (chic.areCompatible()) {
530                chicAttribute = new ChicAttribute(model, name);
531                chicAttribute.setExpression(chic.getCompositeInterface());
532                return true;
533            } else {
534                return false;
535            }
536        }
537
538        return true;
539
540        //        if (!(chicInterface = new String(collectedInterfaces)).equals("")) {
541        //            chicAttribute = new ChicAttribute(model, name);
542        //            chicAttribute.setExpression(chicInterface);
543        //        }
544        //        if (!silent) System.out.println(chicInterface + "\n");
545        //        return true;
546    }
547
548    ///////////////////////////////////////////////////////////////////
549    ////                         private methods                   ////
550    // Sanitize a String so that it is syntactically equivallent to
551    // a (qualified) Java identifier. Characters that are not permitted
552    // in a (qualified) Java identifier are changed to underscores.
553    // This method does not check that the returned string is a
554    // keyword or literal.
555    // Note that two different strings can sanitize to the same
556    // string.
557    private String _sanitizeName(String name) {
558        name = name.replaceAll("[^(\\w|\\$|\\.)]", "_");
559
560        // Substitute all $ with \\$ so that you don't get an exception
561        // from the matcher.
562        return name.replaceAll("\\$", "\\\\\\$");
563    }
564
565    ///////////////////////////////////////////////////////////////////
566    ////                         private variables                 ////
567}