001/*
002 * Copyright (c) 2017 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2017-08-29 15:27:08 -0700 (Tue, 29 Aug 2017) $' 
007 * '$Revision: 1392 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.webview.server;
031
032import java.lang.ref.WeakReference;
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.Map;
036import java.util.concurrent.atomic.AtomicInteger;
037
038import org.kepler.webview.actor.WebViewAttribute;
039
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.NamedObj;
043import ptolemy.kernel.util.Settable;
044import ptolemy.kernel.util.StringAttribute;
045import ptolemy.kernel.util.Workspace;
046
047public class WebViewId extends StringAttribute {
048
049    /** Create a new WebViewId in the specified container. This constructor is private
050     *  since only WebViewId.getId() is allowed to create instances of this class.
051     */
052    private WebViewId(NamedObj container) throws IllegalActionException, NameDuplicationException {
053        super(container, NAME);
054
055        // set a default value for the id.
056        _setExpression(null, false);
057
058        // set visibility to EXPERT since normally users do not change the id.
059        setVisibility(Settable.EXPERT);
060    }
061    
062    /** Clone the WebViewId into the specified workspace. */
063    @Override
064    public Object clone(Workspace workspace) throws CloneNotSupportedException {
065        WebViewId newObject = (WebViewId) super.clone(workspace);
066        // if the id was not set manually, generate a new id for the clone.
067        if(!newObject._setManually) {
068            try {
069                _setExpression(null, false);
070            } catch (IllegalActionException e) {
071                throw new CloneNotSupportedException("Error setting expression to new webview id: " + e.getMessage());
072            }
073        }
074        return newObject;
075    }
076    
077    /** Get the web view id for a NamedObj. If the NamedObj does not have a web view id,
078     *  then a new one is created.
079     */
080    public static String getId(NamedObj namedObj) throws IllegalActionException {
081        WebViewId id = (WebViewId) namedObj.getAttribute(NAME);
082        if(id == null) {
083            try {
084                id = new WebViewId(namedObj);
085            } catch(NameDuplicationException e) {
086                throw new IllegalActionException(namedObj, e, "Error creating WebViewId.");
087            }
088        }
089        
090        String idStr;
091        
092        NamedObj toplevel = namedObj.toplevel();
093        if(toplevel == namedObj) {
094            idStr = id.getExpression();
095        } else {
096            // TODO
097            idStr = getId(toplevel) + "-" + id.getExpression();
098        }
099        
100        // TODO need to delete NamedObjs from map when they are removed from workflow.
101        _idMap.put(idStr, new WeakReference<NamedObj>(namedObj));
102        
103        return idStr;
104    }
105    
106    /** Get the NamedObj for a specific id. */
107    public static NamedObj getNamedObj(String id) {
108        return _idMap.get(id).get();
109    }
110    
111    /** Remove all the ids inside of the a workflow. */
112    public static void removeWorkflow(NamedObj model) {
113        
114        synchronized(_idMap) {
115            Map<String,WeakReference<NamedObj>> mapCopy = new HashMap<String,WeakReference<NamedObj>>(_idMap);
116            for(Map.Entry<String, WeakReference<NamedObj>> entry : mapCopy.entrySet()) {
117                final NamedObj namedObj = entry.getValue().get();
118                if(namedObj == null || namedObj == model || namedObj.toplevel() == model) {
119                    _idMap.remove(entry.getKey());
120                    if(namedObj instanceof WebViewAttribute) {
121                        ((WebViewAttribute)namedObj).unregisterHandler();
122                    }
123                }
124            }
125        }
126    }
127    
128    /** Override the base class to remove an existing WebViewId in the
129     *  container. This can occur since a WebViewId is created during
130     *  the cloning process.
131     */
132    @Override
133    public void setContainer(NamedObj container) throws IllegalActionException, NameDuplicationException {
134        
135        if(container != null) {
136            // see if the container already has an id.
137            // this can occur during cloning since WebViewAttribute.setContainer()
138            // can create an id during registering the handle.
139            WebViewId containerId = (WebViewId) container.getAttribute(NAME);
140            if(containerId != null) {
141                // copy the values
142                _setExpression(containerId.getExpression(), containerId._setManually);
143                // remove the existing one.
144                containerId.setContainer(null);
145            }
146        }
147        
148        super.setContainer(container);
149    }
150    
151    /** Set the value of the id. */
152    @Override
153    public void setExpression(String value) throws IllegalActionException {
154        _setExpression(value, true);
155    }
156    
157    /** Set the value of the ID.
158     *  @param value The value of the id. If null or empty, a new id is generated and setManually
159     *  is ignored and assumed to be false.
160     *  @param setManually If true, then this object is set persistent so that it is saved in the model.
161     */
162    private void _setExpression(String value, boolean setManually) throws IllegalActionException {
163        
164        if(value == null || value.trim().isEmpty()) {
165            value = String.valueOf(_counter.incrementAndGet());
166            setManually = false;
167        }
168        
169        super.setExpression(value);
170        _setManually = setManually;        
171        setPersistent(setManually);
172        //System.out.println("_setExpression to " + value + " done manually = " + setManually);
173     }
174    
175    private boolean _setManually = false;
176        
177    private static final String NAME = "_webViewId";
178    
179    private static final AtomicInteger _counter = new AtomicInteger(0);
180    
181    private static final Map<String,WeakReference<NamedObj>> _idMap =
182            Collections.synchronizedMap(new HashMap<String,WeakReference<NamedObj>>());
183}