001/*
002 * Copyright (c) 2007-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-04-17 21:39:59 +0000 (Tue, 17 Apr 2012) $' 
007 * '$Revision: 29739 $'
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.sdm.spa;
031
032import java.util.List;
033
034import org.jdom.Document;
035import org.jdom.Element;
036import org.jdom.input.DOMBuilder;
037import org.jdom.output.XMLOutputter;
038
039import ptolemy.actor.AtomicActor;
040import ptolemy.actor.TypedIOPort;
041import ptolemy.data.ArrayToken;
042import ptolemy.data.BooleanToken;
043import ptolemy.data.DoubleToken;
044import ptolemy.data.IntToken;
045import ptolemy.data.LongToken;
046import ptolemy.data.StringToken;
047import ptolemy.data.Token;
048import ptolemy.data.UnsignedByteToken;
049import ptolemy.data.XMLToken;
050import ptolemy.data.type.ArrayType;
051import ptolemy.data.type.BaseType;
052import ptolemy.data.type.Type;
053import ptolemy.kernel.util.IllegalActionException;
054
055//////////////////////////////////////////////////////////////////////////
056//// XMLHelper
057
058/**
059 * This splits an XML document into its child elements and each is sent to the
060 * output port with the same name. It is used by both XMLDisassembler and the
061 * new web service actor. (The latter uses this class so that when the web
062 * service does not have complex return types, it can output them directly [like
063 * the original WebServiceActor]).
064 * 
065 * @author Daniel Crawl
066 * @version $Id: XMLHelper.java 29739 2012-04-17 21:39:59Z crawl $
067 */
068
069public class XMLHelper {
070        /**
071         * Initialize helper classes.
072         * 
073         * @param actor
074         *            the using this class
075         */
076        public XMLHelper(AtomicActor actor) {
077                _actor = actor;
078                _outputter = new XMLOutputter();
079                _builder = new DOMBuilder();
080        }
081
082        /** Set the value of _outputNilVal. */
083        public void setOutputNil(boolean val) {
084                _outputNilVal = val;
085        }
086
087        /** Set the value of _arraysWrappedVal. */
088        public void setArraysWrapped(boolean val) {
089                _arraysWrappedVal = val;
090        }
091
092        /**
093         * Peel off the root element from an XML document, and output the content in
094         * each child element to the output port with same name.
095         * 
096         * @param name
097         *            the root element name in the XML document to peel off
098         * @param doc
099         *            the XML document
100         * @param outputPorts
101         *            the output ports
102         * @param portPrepend
103         *            optional string to remove from beginning of port name
104         */
105        public void splitOutXML(String name, org.w3c.dom.Document doc,
106                        List outputPorts, String portPrepend) throws IllegalActionException {
107                Document jdomDoc = _builder.build(doc);
108                Element rootEl = jdomDoc.getRootElement();
109
110                /*
111                 * System.out.println("peeling for " + name);
112                 * System.out.println("rootel name " + rootEl.getName());
113                 * System.out.println("xml = " + _outputter.outputString(jdomDoc));
114                 */
115
116                // remove all the name spaces so the getChildren() call below
117                // retrieves all the children
118                List k = rootEl.getChildren();
119                for (int z = 0; z < k.size(); z++) {
120                        Element e = (Element) k.get(z);
121                        e.setNamespace(null);
122                        // System.out.println("rm ns for " + e.getName());
123                        // System.out.println("z " + z + " name |" + e.getName() + "|");
124                        // System.out.println("z " + z + " vat " + e.getText());
125                }
126
127                // make sure document root element name matches port name
128                if (!rootEl.getName().equals(name)) {
129                        throw new IllegalActionException(_actor, "No xml input with name "
130                                        + name);
131                }
132
133                // output each part to the corresponding port
134                Object[] portArray = outputPorts.toArray();
135                for (int i = 0; i < portArray.length; i++) {
136                        TypedIOPort port = (TypedIOPort) portArray[i];
137                        String portName = port.getName();
138                        Type portType = port.getType();
139
140                        Token token = null;
141
142                        // see if the port's name begins with the port prepend string
143                        if (portPrepend != null && portName.startsWith(portPrepend)) {
144                                // remove port prepend string from port name
145                                portName = portName.substring(portPrepend.length());
146                        }
147
148                        // System.out.println("looking for child " + portName);
149
150                        // get the children matching this port name
151                        List kids = rootEl.getChildren(portName);
152
153                        if (kids.size() == 0) {
154                                System.out.println("WARNING: no xml child for " + portName);
155                        } else if (((Element) kids.get(0)).getAttribute("href") != null) {
156                                System.out
157                                                .println("WARNING: multi-reference values not supported.");
158                        } else if (portType instanceof ArrayType) {
159                                Type subType = ((ArrayType) portType).getElementType();
160
161                                // see if wrapped
162                                if (_arraysWrappedVal) {
163                                        // remove the additional element
164                                        kids = ((Element) kids.get(0)).getChildren();
165                                }
166
167                                Token[] array = new Token[kids.size()];
168                                if(array.length == 0) {
169                                    token = new ArrayToken(subType);
170                                } else {
171                                for (int j = 0; j < kids.size(); j++) {
172                                        array[j] = _makeToken(subType, (Element) kids.get(j));
173                                }
174                                token = new ArrayToken(array);
175                                }
176                        } else if (kids.size() > 1) {
177                                throw new IllegalActionException(_actor, "XML part " + portName
178                                                + " contained array, but port is not arrayType");
179                        } else {
180                                token = _makeToken(portType, (Element) kids.get(0));
181                        }
182
183                        if (token == null) {
184                                System.out.println("null token");
185                        } else {
186                                port.broadcast(token);
187                        }
188                }
189        }
190
191        /**
192         * Create a token from an Element matching the type of output port.
193         * 
194         * @param portType
195         *            the type
196         * @param child
197         *            the data
198         * @return the token
199         */
200        private Token _makeToken(Type portType, Element child)
201                        throws IllegalActionException {
202                Token retval = null;
203
204                // see if it is nil
205                String nilStr = child.getAttributeValue("nil");
206                if (nilStr != null && nilStr.equals("true")) {
207                        if (!_outputNilVal) {
208                                throw new IllegalActionException(_actor,
209                                                "Got nil value but not configured to output them.");
210                        } else {
211                                retval = Token.NIL;
212                        }
213                } else {
214                        String valStr = null;
215
216                        // get a string of the data
217                        if (portType == BaseType.XMLTOKEN) {
218                                valStr = "<?xml version=\"1.0\"?>"
219                                                + _outputter.outputString(child);
220                        } else {
221                                valStr = child.getText();
222                        }
223
224                        // System.out.println("make token " + portType + " for "
225                        // + child.getName() + " : " + valStr);
226
227                        // create a token based on the type using the data
228                        if (portType == BaseType.STRING) {
229                                retval = new StringToken(valStr);
230                        } else if (portType == BaseType.BOOLEAN) {
231                                retval = new BooleanToken(valStr);
232                        } else if (portType == BaseType.INT) {
233                                retval = new IntToken(valStr);
234                        } else if (portType == BaseType.DOUBLE) {
235                                retval = new DoubleToken(valStr);
236                        } else if (portType == BaseType.LONG) {
237                                retval = new LongToken(valStr);
238                        } else if (portType == BaseType.UNSIGNED_BYTE) {
239                                retval = new UnsignedByteToken(valStr);
240                        } else if (portType == BaseType.XMLTOKEN) {
241                                try {
242                                        retval = new XMLToken(valStr);
243                                } catch (Exception e) {
244                                        // e.printStackTrace();
245                                        throw new IllegalActionException(_actor,
246                                                        "XMLToken constructor exception: " + e.getMessage());
247                                }
248                        }
249                }
250                return retval;
251        }
252
253        // /////////////////////////////////////////////////////////////////
254        // // private members ////
255
256        // whether we output nil tokens for nil="true" attributes
257        private boolean _outputNilVal = false;
258
259        // whether array elements are wrapped in an additional element
260        private boolean _arraysWrappedVal = false;
261
262        // used to create jdom documents from dom documents.
263        private DOMBuilder _builder = null;
264
265        // used to get the string value of an Element
266        private XMLOutputter _outputter = null;
267
268        // the actor that's using this class
269        private AtomicActor _actor = null;
270}