001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2012-05-05 01:42:47 +0000 (Sat, 05 May 2012) $' 007 * '$Revision: 29810 $' 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.io.StringWriter; 033import java.io.Writer; 034 035import org.apache.xml.serialize.OutputFormat; 036import org.apache.xml.serialize.SerializerFactory; 037import org.apache.xml.serialize.XMLSerializer; 038import org.apache.xpath.XPathAPI; 039import org.w3c.dom.Document; 040import org.w3c.dom.Element; 041import org.w3c.dom.Node; 042import org.w3c.dom.NodeList; 043 044import ptolemy.actor.TypedIOPort; 045import ptolemy.actor.lib.Transformer; 046import ptolemy.actor.parameters.PortParameter; 047import ptolemy.data.ArrayToken; 048import ptolemy.data.StringToken; 049import ptolemy.data.XMLToken; 050import ptolemy.data.type.ArrayType; 051import ptolemy.data.type.BaseType; 052import ptolemy.kernel.CompositeEntity; 053import ptolemy.kernel.util.IllegalActionException; 054import ptolemy.kernel.util.NameDuplicationException; 055 056////////////////////////////////////////////////////////////////////////// 057//// XPath 058/** 059 * XPath selects XML nodes based on the XPath syntax. 060 * 061 * This actor takes in an XPath string and an XMLToken, and it returns an array 062 * of XMLTokens. 063 * 064 * @author xiaowen 065 * @version $Id: XPath.java 29810 2012-05-05 01:42:47Z crawl $ 066 */ 067 068public class XPath extends Transformer { 069 070 /** 071 * Construct an XPath actor with the given container and name. 072 * 073 * @param container 074 * The container. 075 * @param name 076 * The name of this actor. 077 * @exception IllegalActionException 078 * If the actor cannot be contained by the proposed 079 * container. 080 * @exception NameDuplicationException 081 * If the container already has an actor with this name. 082 */ 083 public XPath(CompositeEntity container, String name) 084 throws NameDuplicationException, IllegalActionException { 085 086 super(container, name); 087 088 portXPath = new PortParameter(this, "xpath"); 089 090 all = new TypedIOPort(this, "all", false, true); 091 092 input.setTypeEquals(BaseType.XMLTOKEN); 093 output.setTypeEquals(new ArrayType(BaseType.XMLTOKEN)); 094 all.setTypeEquals(new ArrayType(BaseType.STRING)); 095 portXPath.setTypeEquals(BaseType.STRING); 096 097 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" " 098 + "width=\"60\" height=\"20\" " + "style=\"fill:white\"/>\n" 099 + "</svg>\n"); 100 101 } 102 103 // ///////////////////////////////////////////////////////////////// 104 // // ports and parameters //// 105 106 /** The XPath expression. */ 107 public PortParameter portXPath; 108 109 /** The text resulting from applying the XPath expression. */ 110 public TypedIOPort all; 111 112 // ///////////////////////////////////////////////////////////////// 113 // // public methods //// 114 115 116 /** 117 * Take in an XMLToken and the XPath expression, and return an ArrayToken 118 * containing XMLTokens representing the result of selecting nodes using the 119 * XPath expression. 120 * 121 * @exception IllegalActionException 122 * If it can't select nodes using the XPath expression or if 123 * it's unable to create the resulting XMLTokens. 124 */ 125 public void fire() throws IllegalActionException { 126 super.fire(); 127 128 // get inputs 129 XMLToken tokenXml = (XMLToken) input.get(0); 130 portXPath.update(); 131 String strXPath = ((StringToken) portXPath.getToken()).stringValue(); 132 _debug("The XPath expression is: " + strXPath); 133 134 XMLToken xmlTokens[] = null; 135 StringToken stringTokens[] = null; 136 137 // run XPath on it 138 Document document = tokenXml.getDomTree(); 139 if(document == null) { 140 throw new IllegalActionException(this, "Input has no XML: " + tokenXml); 141 } 142 143 synchronized(document) { 144 145 Element element = document.getDocumentElement(); 146 if(element == null) { 147 throw new IllegalActionException(this, "No document element in: " + tokenXml); 148 } 149 Node nodeRoot = element.getFirstChild(); 150 NodeList nodeHits = null; 151 152 try { 153 nodeHits = XPathAPI.selectNodeList(nodeRoot, strXPath); 154 } catch (javax.xml.transform.TransformerException e) { 155 throw new IllegalActionException(this, "XPath: could not select nodes."); 156 } catch(NullPointerException e2) { 157 throw new IllegalActionException(this, "Error selecting node list for: " + tokenXml); 158 } 159 160 // format the results 161 int numXMLTokens = 0; 162 xmlTokens = new XMLToken[nodeHits.getLength()]; 163 stringTokens = new StringToken[nodeHits.getLength()]; 164 165 SerializerFactory serializerFactory = SerializerFactory 166 .getSerializerFactory("xml"); 167 OutputFormat outputFormat = new OutputFormat(); 168 outputFormat.setOmitXMLDeclaration(true); 169 XMLSerializer xmlSerializer = (XMLSerializer) serializerFactory 170 .makeSerializer(outputFormat); 171 172 for (int i = 0; i < nodeHits.getLength(); i++) { 173 Writer stringWriter = new StringWriter(); 174 xmlSerializer.setOutputCharStream(stringWriter); 175 176 Node node = nodeHits.item(i); 177 178 if (node.getNodeType() == Node.ELEMENT_NODE) { 179 numXMLTokens++; 180 try { 181 xmlSerializer.serialize((Element) node); 182 } catch (java.io.IOException e) { 183 throw new IllegalActionException(this, 184 "XPath: java.io.IOException ..."); 185 } 186 187 String xmlStr = stringWriter.toString(); 188 189 try { 190 xmlTokens[i] = new XMLToken(xmlStr); 191 } catch (java.lang.Exception e) { 192 throw new IllegalActionException(this, 193 "XPath: unable to create XMLToken with string: " 194 + stringWriter.toString()); 195 } 196 197 stringTokens[i] = new StringToken(xmlStr); 198 } else if (node.getNodeType() == Node.ATTRIBUTE_NODE 199 || node.getNodeType() == Node.TEXT_NODE) { 200 stringTokens[i] = new StringToken(node.getNodeValue()); 201 } 202 } 203 204 // If there were no results, then send out a dummy xml token. 205 // This is really a hack. The ideal solution for this actor would be 206 // able to send out an empty ArrayToken. 207 if (numXMLTokens < xmlTokens.length) { 208 xmlTokens = new XMLToken[1]; 209 try { 210 xmlTokens[0] = new XMLToken("<dummy/>"); 211 } catch (java.lang.Exception e) { 212 // well this really shouldn't happen. 213 throw new IllegalActionException(this, 214 "XPath: '<dummy/>' apparently isn't valid XML"); 215 } 216 } 217 218 } 219 220 // send out the results 221 if(xmlTokens.length == 0) { 222 output.send(0, new ArrayToken(BaseType.XMLTOKEN)); 223 } else { 224 output.send(0, new ArrayToken(xmlTokens)); 225 } 226 227 // if no results, send empty array 228 if (stringTokens.length == 0) { 229 all.broadcast(new ArrayToken(BaseType.STRING)); 230 } else { 231 all.broadcast(new ArrayToken(stringTokens)); 232 } 233 } 234 235}