1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.releng.generators.rss;
12 
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.io.IOException;
16 import java.io.PrintStream;
17 import java.util.Date;
18 
19 import javax.xml.parsers.DocumentBuilder;
20 import javax.xml.parsers.DocumentBuilderFactory;
21 import javax.xml.parsers.ParserConfigurationException;
22 import javax.xml.transform.Transformer;
23 import javax.xml.transform.TransformerException;
24 import javax.xml.transform.dom.DOMSource;
25 import javax.xml.transform.stream.StreamResult;
26 import javax.xml.xpath.XPath;
27 import javax.xml.xpath.XPathConstants;
28 import javax.xml.xpath.XPathExpressionException;
29 import javax.xml.xpath.XPathFactory;
30 
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.Task;
33 import org.apache.tools.ant.util.DateUtils;
34 import org.w3c.dom.Attr;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.Node;
38 import org.w3c.dom.NodeList;
39 import org.w3c.dom.Text;
40 import org.xml.sax.SAXException;
41 
42 import org.eclipse.releng.util.rss.Messages;
43 import org.eclipse.releng.util.rss.RSSFeedUtil;
44 
45 /**
46  * Parameters:
47  *   debug - more output to console - eg., 0|1|2
48  *
49  *   file - path to the XML file that will be read - eg., /path/to/file.to.read.xml
50  *   xpath - xpath string representing the object to modify
51  *   replacement - string to use as replacement
52  *
53  * @author nickb
54  *
55  */
56 public class RSSFeedUpdateEntryTask extends Task {
57 
58   private int debug = 0;
59 
60   private static final String now = getTimestamp();
61 
62   private static final XPath xp = XPathFactory.newInstance().newXPath();
63 
64   private static final String NS = ""; //$NON-NLS-1$
65   private static final String SEP = "----"; //$NON-NLS-1$
66   private static final String SP = " "; //$NON-NLS-1$
67 
68   //required fields
69   private File file;
70 
71   private String xpath;
72   private String replacement;
73 
74   private Transformer transformer = null;
75 
76   private boolean isNodeFound = false;
77   private boolean isNodeChanged = false;
78   private Node foundNode = null;
79 
80   //optional
setDebug(int debug)81   public void setDebug(int debug) { this.debug = debug; }
82 
83   //required fields
setFile(String file)84   public void setFile(String file) {
85     if (isNullString(file))
86     { System.err.println(Messages.getString("RSSFeedCommon.FileError")); } //$NON-NLS-1$
87     else
88     { this.file = new File(file); }
89   }
setXpath(String xpath)90   public void setXpath(String xpath) {
91     if (isNullString(xpath))
92     { System.err.println(Messages.getString("RSSFeedCommon.XpathError")); } //$NON-NLS-1$
93     else
94     { this.xpath = xpath; }
95   }
96 
97   //optional - if null, display value found instead of changing it - see RSSFeedGetPropertyTask
setReplacement(String replacement)98   public void setReplacement(String replacement) { this.replacement = replacement; }
99 
100   // The method executing the task
execute()101   public void execute() throws BuildException {
102     if (debug>0) {
103       System.out.println(Messages.getString("RSSFeedUpdateEntryTask.SearchingFor") + SP + xpath + (!isNullString(replacement)?", " + Messages.getString("RSSFeedUpdateEntryTask.ReplacingWith") + " '" + replacement + "'":NS)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
104     }
105     updateFeedXML(file); // load previous
106   }
107 
108   //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
updateFeedXML(File file)109   private void updateFeedXML(File file){
110     if (file.exists()) {
111       DocumentBuilderFactory documentBuilderFactory=DocumentBuilderFactory.newInstance();
112       documentBuilderFactory.setNamespaceAware(true);
113       DocumentBuilder documentBuilder=null;
114       try {
115         documentBuilder=documentBuilderFactory.newDocumentBuilder();
116       }
117       catch (ParserConfigurationException e) {
118         e.printStackTrace();
119       }
120       Document document=null;
121       try {
122         document=documentBuilder.parse(file);
123       }
124       catch (SAXException e) {
125         e.printStackTrace();
126       }
127       catch (IOException e) {
128         e.printStackTrace();
129       }
130 
131       try {
132         transformer = RSSFeedAddEntryTask.createTransformer("UTF-8"); //$NON-NLS-1$
133       } catch (TransformerException e) {
134         e.printStackTrace();
135       }
136 
137       if (!isNullString(replacement)) {
138         setEntryNodeUpdate(document.getDocumentElement());
139       }
140       Node newNode=findAndReplace(document);
141       if (debug > 1 && newNode != null) {
142         try {
143           System.out.println(SEP);
144           transformer.transform(new DOMSource(newNode),new StreamResult(System.out));
145           System.out.println(SEP);
146         }
147         catch (TransformerException e) {
148           e.printStackTrace();
149         }
150       }
151       if (!isNullString(replacement) && newNode != null) {
152         try {
153           transformer.transform(new DOMSource(document),new StreamResult(new PrintStream(file)));
154         }
155         catch (FileNotFoundException e) {
156           e.printStackTrace();
157         }
158         catch (TransformerException e) {
159           e.printStackTrace();
160         }
161       }
162     }
163     else {
164       System.out.println(Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString()+ " "+ Messages.getString("RSSFeedUpdateEntryTask.DoesNotExist")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
165     }
166   }
167 
168   // get/set the desired node
getFoundNode()169   public Node getFoundNode()
170   {
171     return this.foundNode;
172   }
setFoundNode(Node foundNode)173   private void setFoundNode(Node foundNode)
174   {
175     this.foundNode = foundNode;
176   }
177 
178   // has the desired node been found?
getNodeFound()179   public boolean getNodeFound()
180   {
181     return this.isNodeFound;
182   }
setNodeFound(boolean isNodeFound)183   private void setNodeFound(boolean isNodeFound)
184   {
185     this.isNodeFound = isNodeFound;
186   }
187 
188   // has the desired node been changed?
getNodeChanged()189   public boolean getNodeChanged()
190   {
191     return this.isNodeChanged;
192   }
setNodeChanged(boolean isNodeChanged)193   private void setNodeChanged(boolean isNodeChanged)
194   {
195     this.isNodeChanged = isNodeChanged;
196   }
197 
198   /**
199    * Modify an entry:
200    *
201    *   <entry>
202    *      <title/>
203    *      <link href=""/>
204    *      <id/>
205    *      <updated/>
206    *      <summary>
207    *       ...
208    *     </summary>
209    *   </entry>
210    */
findAndReplace(Document document)211   private Node findAndReplace(Document document) {
212     Node parentEntryNode = null;
213     Node aNode = null;
214     if (debug==0) { System.out.print(xpath + (isNullString(replacement)?" = ":" :: ")); } //$NON-NLS-1$ //$NON-NLS-2$
215     NodeList nodelist = getNodeList(document, xpath);
216     // Process the elements in the nodelist
217     if (nodelist != null && nodelist.getLength()>0) {
218       for (int i=0; i<nodelist.getLength(); i++) {
219         Node node = (Node)nodelist.item(i);
220         switch (node.getNodeType())
221         {
222           case Node.ATTRIBUTE_NODE :
223             aNode = (Attr)nodelist.item(i);
224             if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundAttribute")); }  //$NON-NLS-1$
225             break;
226 
227           case Node.ELEMENT_NODE :
228             aNode = (Element)nodelist.item(i);
229             if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundElement")); } //$NON-NLS-1$
230             break;
231 
232           case Node.TEXT_NODE :
233             aNode = (Text)nodelist.item(i);
234             if (debug>0) { System.out.print(Messages.getString("RSSFeedUpdateEntryTask.DebugFoundText")); } //$NON-NLS-1$
235             break;
236 
237           default:
238             aNode = null;
239           break;
240         }
241         if (aNode != null) {
242           setFoundNode(aNode);
243           setNodeFound(true);
244           System.out.print((debug>0?aNode.getNodeName() + " = ":NS) + aNode.getNodeValue()); //$NON-NLS-1$ //$NON-NLS-2$
245           if (!isNullString(replacement)) { aNode.setTextContent(replacement); }
246           System.out.println(isNullString(replacement)?NS:" => " + replacement); //$NON-NLS-1$ //$NON-NLS-2$
247           if (debug>0) {
248             try
249             {
250               // write to console
251               System.out.println(SEP); //$NON-NLS-1$
252               transformer.transform(new DOMSource(getParentNode(document,aNode,null,NS)), new StreamResult(System.out));  //$NON-NLS-1$
253               System.out.println(SEP); //$NON-NLS-1$
254             }
255             catch (TransformerException e)
256             {
257               e.printStackTrace();
258             }
259           }
260           if (!isNullString(replacement)) {
261             parentEntryNode = getParentNode(document, aNode, "entry", NS); //$NON-NLS-1$ //$NON-NLS-2$
262             setEntryNodeUpdate(parentEntryNode);
263           }
264         }
265       }
266     } else {
267       System.out.println(Messages.getString("RSSFeedUpdateEntryTask.XpathNodeNotFound")); //$NON-NLS-1$
268     }
269     return parentEntryNode;
270   }
271 
getParentNode(Document document, Node nodeIn, String target, String indent)272   private Node getParentNode(Document document, Node nodeIn, String target, String indent)
273   {
274     Node node = nodeIn;
275     if (node.getNodeType() != Node.ELEMENT_NODE) {
276       if (debug>1) { System.out.println(indent + Messages.getString("RSSFeedUpdateEntryTask.DebugGotATNode") + node.getNodeName()); } //$NON-NLS-1$
277       // get the element for the attrib/text node
278       NodeList nodelist = getNodeList(document, xpath.substring(0, xpath.lastIndexOf("/")));
279       if (nodelist !=null && nodelist.getLength()>0)
280       {
281         for (int i=0; i<nodelist.getLength(); i++) {
282           node = (Node)nodelist.item(i);
283           break;
284         }
285       }
286     }
287     if (debug>1) { System.out.println(indent + Messages.getString("RSSFeedUpdateEntryTask.DebugGotENode") + node.getNodeName() + " (" + node.getNodeType() + ")"); } //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
288     if (!isNullString(target) && !node.getNodeName().equals(target))
289     {
290       node = getParentNode(document, node.getParentNode(), target, indent + "_ "); //$NON-NLS-1$
291     }
292     return node;
293   }
294 
getNodeList(Document document, String xpath)295   private NodeList getNodeList(Document document, String xpath)
296   {
297     NodeList nodelist = null;
298     try
299     {
300       xp.reset();
301       Object o = xp.evaluate(xpath, document, XPathConstants.NODESET);
302       if (o instanceof NodeList)
303       {
304         nodelist = (NodeList)o;
305       }
306     }
307     catch (XPathExpressionException e)
308     {
309       e.printStackTrace();
310     }
311     return nodelist;
312   }
313 
314   //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
setEntryNodeUpdate(Node parentEntryNode)315   private void setEntryNodeUpdate(Node parentEntryNode){
316     for (Node child=parentEntryNode.getFirstChild(); child != null; child=child.getNextSibling()) {
317       if ("updated".equals(child.getLocalName())) { //$NON-NLS-1$
318         if (debug > 0) {
319           System.out.println(Messages.getString("RSSFeedCommon.Set") + " <" + child.getLocalName()+ ">"+ now+ "</"+ child.getLocalName()+ ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
320         }
321         ((Element)child).setTextContent(now);
322         setNodeChanged(true);
323         break;
324       }
325     }
326   }
327 
328 
getTimestamp()329   private static String getTimestamp() { // eg., 2006-04-10T20:40:08Z
330     return DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN) + "Z";  //$NON-NLS-1$
331   }
332 
isNullString(String str)333   private static boolean isNullString(String str)
334   {
335     return RSSFeedUtil.isNullString(str);
336   }
337 
338 }