• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //TODO: bug - can't run CreateFeed and AddEntry together when debug=2 - file locking problem?
14 
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.OutputStreamWriter;
20 import java.util.Date;
21 
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
24 import javax.xml.parsers.ParserConfigurationException;
25 import javax.xml.transform.OutputKeys;
26 import javax.xml.transform.Transformer;
27 import javax.xml.transform.TransformerException;
28 import javax.xml.transform.TransformerFactory;
29 import javax.xml.transform.dom.DOMSource;
30 import javax.xml.transform.stream.StreamResult;
31 
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.Task;
34 import org.apache.tools.ant.util.DateUtils;
35 
36 import org.eclipse.releng.util.rss.Messages;
37 import org.eclipse.releng.util.rss.RSSFeedUtil;
38 
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.Node;
42 import org.xml.sax.SAXException;
43 
44 /**
45  * Parameters:
46  *   debug      - more output to console - eg., 0|1|2
47  *
48  *   file       - path to the XML file that will be created - eg., /path/to/file.to.create.xml
49  *   project    - project's name, used to label the feed - eg., Eclipse, EMF, UML2
50  *   branch     - build's branch, eg., 2.2.0
51  *   buildID    - build's ID, eg., S200605051234
52  *   feedURL    - URL of the feed where it will be published - eg., http://servername/path/to/feed.xml
53  *      note that feedURL is not required if the feed already exists, only if a new feed file must be created
54  *   buildURL   - URL of the build being added to the feed - eg., http://servername/path/to/project/branch/buildID/
55  *
56  *   buildAlias - build's alias, eg., 2.2.0RC2
57  *
58  *   dependencyURLs   - upstream dependencies, eg., UML2 depends on emf and eclipse, so specify TWO URLs in properties file or ant task
59  *
60  *   releaseNotesURL  - URL of the build's release notes page - eg., http://www.eclipse.org/project/news/release-notes.php
61  *   updateManagerURL - URL of the build's Update Manager site - eg., http://servername/path/to/project/updates/
62  *   downloadsURL     - URL of the build's downloads - eg., http://servername/path/to/project/downloads/
63  *
64  *   jarSigningStatus - code to define jar signing status - eg., one of:
65  *      NONE (or '')  - no status available or not participating
66  *      UNSIGNED      - no jar signage available or done yet
67  *      SIGNREADY     - jars promoted to eclipse.org, ready for signing
68  *      BUILDREADY    - signed on eclipse.org, ready to be collected and bundled as zips and copied to UM site
69  *      SIGNED        - signed & bundled on download page and on UM site
70  *
71  *   callistoStatus   - code to define Callisto status, eg., one of:
72  *      NONE (or '')         - not part of Callisto or unknown status
73  *      BUILDCOMPLETE        - Have you finished your RC1 bits?
74  *      2006-05-02T20:50:00Z - When do you expect to finish them?
75  *      TPTP                 - If you're waiting for another project, which one(s)? (TPTP is just an example)
76  *      UMSITEREADY          - Have you placed those bits in your update site?
77  *      CALLISTOSITEREADY    - Have you updated the features.xml file in the Callisto CVS directory?
78  *      COMPLETE             - Are you ready for RC1 to be declared?
79  *
80  *   buildType - code to define type of build, eg., one of:
81  *      N      - Nightly
82  *      I      - Integration
83  *      M      - Maintenance
84  *      S      - Stable (Milestone or Release Candidate)
85  *      R      - Release
86  *      MC     - Maintenance-Callisto
87  *      SC     - Stable-Callisto
88  *      RC     - Release-Callisto
89  *
90  *   Releases           - comma or space-separated list of releases in quints of os,ws,arch,type/name,filename,...
91  *                      - eg., win32,win,x86,SDK,eclipse-SDK-3.2RC5-win32.zip,linux,gtk,x86_64,SDK,eclipse-SDK-3.2RC5-linux-gtk.tar.gz
92  *                      - (for examples and definitions of ws, os + arch, see below)
93  *
94  *   JUnitTestURL       - URL of the build's JUnit test results - eg., http://servername/path/to/project/branch/buildID/testResults.php
95  *   performanceTestURL - URL of the build's performance tests - eg., http://servername/path/to/project/branch/buildID/performance/performance.php
96  *   APITestURL         - URL of the build's API test results - eg., http://servername/path/to/project/branch/buildID/testResults.php
97  *
98  *   JUnitTestResults       - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,x86,PASS,linux,gtk,x86,PASS
99  *   performanceTestResults - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,x86_64,PASS,linux,gtk,x86_64,PASS
100  *   APITestResults         - comma or space-separated list of test results in quads of os,ws,arch,status,os,ws,status,arch,... - eg., win32,win,ppc,PASS,linux,gtk,ppc,PASS
101  *      ws     - window system - eg., ALL, win32, win64, linux, macos...
102  *      os     - operating system - eg., ALL, win, gtk, motif, carbon, ...
103  *      arch   - architecture, eg., ALL, x86, x86_64, ppc, ...
104  *      status - status code for test results - eg., one of: PASS, PENDING, FAIL, UNKNOWN, SKIPPED
105  *
106  * @author nickb
107  *
108  */
109 public class RSSFeedAddEntryTask extends Task {
110 
111   private int debug = 0;
112 
113   private static final String now = getTimestamp();
114 
115   //$ANALYSIS-IGNORE codereview.java.rules.portability.RulePortabilityLineSeparators
116   private static final String NL="\n"; //$NON-NLS-1$
117   private static final String NS = ""; //$NON-NLS-1$
118   private static final String SEP = "----"; //$NON-NLS-1$
119   private static final String SP = " "; //$NON-NLS-1$
120 
121   private static final String splitter = "[,\t " + NL + "]+"; //$NON-NLS-1$ //$NON-NLS-2$
122 
123   //required fields
124   private File file;
125   private String project;
126   private String branch;
127   private String buildID;
128   private String feedURL;
129   private String buildURL;
130 
131   //optional
132   private String buildAlias;
133 
134   //optional
135   private String[] dependencyURLs = new String[] {};
136 
137   //optional
138   private String releaseNotesURL;
139   private String updateManagerURL;
140   private String downloadsURL;
141   private String jarSigningStatus;
142   private String callistoStatus;
143   private String buildType;
144 
145   //optional
146   private String[] releases = new String[] {};
147 
148   //optional
149   private String JUnitTestURL;
150   private String performanceTestURL;
151   private String APITestURL;
152   private String[] JUnitTestResults;
153   private String[] performanceTestResults;
154   private String[] APITestResults;
155 
156   //optional
setDebug(int debug)157   public void setDebug(int debug) { this.debug = debug; }
158 
159   //required fields
setFile(String file)160   public void setFile(String file) {
161     if (isNullString(file))
162     { System.err.println(Messages.getString("RSSFeedCommon.FileError")); }  //$NON-NLS-1$
163     else
164     { this.file = new File(file); }
165   }
setProject(String project)166   public void setProject(String project) {
167     if (isNullString(project))
168     { System.err.println(Messages.getString("RSSFeedCommon.ProjectError")); }  //$NON-NLS-1$
169     else
170     { this.project = project; }
171   }
setBranch(String branch)172   public void setBranch(String branch) {
173     if (isNullString(branch))
174     { System.err.println(Messages.getString("RSSFeedAddEntryTask.BranchError")); }  //$NON-NLS-1$
175     else
176     { this.branch = branch; }
177   }
setBuildID(String buildID)178   public void setBuildID(String buildID) {
179     if (isNullString(buildID))
180     { System.err.println(Messages.getString("RSSFeedAddEntryTask.BuildIDError")); }  //$NON-NLS-1$
181     else
182     { this.buildID = buildID; }
183   }
setFeedURL(String feedURL)184   public void setFeedURL(String feedURL) {
185     if (isNullString(feedURL))
186     { System.err.println(Messages.getString("RSSFeedCommon.FeedURLError")); }  //$NON-NLS-1$
187     else
188     { this.feedURL = feedURL; }
189   }
setBuildURL(String buildURL)190   public void setBuildURL(String buildURL) {
191     if (isNullString(buildURL))
192     { System.err.println(Messages.getString("RSSFeedAddEntryTask.BuildURLError")); }  //$NON-NLS-1$
193     else
194     { this.buildURL = buildURL; }
195   }
196 
197   //optional: alias is usually something like "3.2.0M6"
setBuildAlias(String buildAlias)198   public void setBuildAlias(String buildAlias) { this.buildAlias = buildAlias; }
199 
200   //optional: upstream dependencies, eg., UML2 depends on emf and eclipse, so specify TWO URLs in properties file or ant task
setDependencyURLs(String dependencyURLs)201   public void setDependencyURLs(String dependencyURLs) { if (!isNullString(dependencyURLs)) { this.dependencyURLs = dependencyURLs.split(splitter); } }
202 
203   //optional: define releases available in this build for a series of operating systems, windowing systems, and type
setReleases(String releases)204   public void setReleases(String releases) { if (!isNullString(releases)) { this.releases = releases.split(splitter); } }
205 
206   //optional: informational links to release notes, downloads, update manager
setReleaseNotesURL(String releaseNotesURL)207   public void setReleaseNotesURL(String releaseNotesURL) { this.releaseNotesURL = releaseNotesURL; }
setUpdateManagerURL(String updateManagerURL)208   public void setUpdateManagerURL(String updateManagerURL) { this.updateManagerURL = updateManagerURL; }
setDownloadsURL(String downloadsURL)209   public void setDownloadsURL(String downloadsURL) { this.downloadsURL = downloadsURL; }
setJarSigningStatus(String jarSigningStatus)210   public void setJarSigningStatus(String jarSigningStatus) { this.jarSigningStatus = jarSigningStatus; }
setCallistoStatus(String callistoStatus)211   public void setCallistoStatus(String callistoStatus) { this.callistoStatus = callistoStatus; }
setBuildType(String buildType)212   public void setBuildType(String buildType) {
213     if (!isNullString(buildType))
214     {
215       this.buildType = buildType;
216     }
217     else
218     {
219       this.buildType = buildID.replaceAll("[^NIMSR]", NS); //$NON-NLS-1$
220       if (this.buildType.length()>1)
221       {
222         this.buildType=this.buildType.substring(0, 1);
223       }
224     }
225 
226   }
227 
228   //optional: test URLs and results
setJUnitTestURL(String JUnitTestURL)229   public void setJUnitTestURL(String JUnitTestURL) { this.JUnitTestURL = JUnitTestURL; }
setPerformanceTestURL(String performanceTestURL)230   public void setPerformanceTestURL(String performanceTestURL) { this.performanceTestURL = performanceTestURL; }
setAPITestURL(String APITestURL)231   public void setAPITestURL(String APITestURL) { this.APITestURL = APITestURL; }
setJUnitTestResults(String JUnitTestResults)232   public void setJUnitTestResults(String JUnitTestResults) { if (!isNullString(JUnitTestResults)) { this.JUnitTestResults = JUnitTestResults.split(splitter); } }
setPerformanceTestResults(String performanceTestResults)233   public void setPerformanceTestResults(String performanceTestResults) { if (!isNullString(performanceTestResults)) { this.performanceTestResults = performanceTestResults.split(splitter); } }
setAPITestResults(String APITestResults)234   public void setAPITestResults(String APITestResults) { if (!isNullString(APITestResults)) { this.APITestResults = APITestResults.split(splitter); } }
235 
236   // The method executing the task
execute()237   public void execute() throws BuildException {
238     if (debug>0) {
239       System.out.println(Messages.getString("RSSFeedAddEntryTask.AddingEntryTo") + project + SP + Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString() + ", " + Messages.getString("RSSFeedCommon.ToBePublishedAt") + feedURL); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
240     }
241     updateFeedXML(file); // load previous
242   }
243 
244   //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
updateFeedXML(File file)245   private void updateFeedXML(File file){
246     if (!file.exists()) {
247       System.out.println(Messages.getString("RSSFeedCommon.RSSFeedFile") + SP + file.toString() + SP + Messages.getString("RSSFeedAddEntryTask.DoesNotExist")); //$NON-NLS-1$ //$NON-NLS-2$
248       RSSFeedCreateFeedTask creator=new RSSFeedCreateFeedTask();
249       creator.setFile(file.toString());
250       creator.setFeedURL(feedURL);
251       creator.setProject(project);
252       creator.setDebug(debug);
253       creator.execute();
254     }
255     DocumentBuilderFactory documentBuilderFactory=DocumentBuilderFactory.newInstance();
256     documentBuilderFactory.setNamespaceAware(true);
257     DocumentBuilder documentBuilder=null;
258     try {
259       documentBuilder=documentBuilderFactory.newDocumentBuilder();
260     }
261     catch (ParserConfigurationException e) {
262       e.printStackTrace();
263     }
264     Document document=null;
265     try {
266       document=documentBuilder.parse(file);
267     }
268     catch (SAXException e) {
269       e.printStackTrace();
270     }
271     catch (IOException e) {
272       e.printStackTrace();
273     }
274 
275     Transformer transformer = null;
276     try {
277       transformer = createTransformer("UTF-8"); //$NON-NLS-1$
278     } catch (TransformerException e) {
279       e.printStackTrace();
280     }
281 
282     Element element=document.getDocumentElement();
283     for (Node child=element.getFirstChild(); child != null; child=child.getNextSibling()) {
284       if ("updated".equals(child.getLocalName())) { //$NON-NLS-1$
285         if (debug > 0) {
286           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$
287         }
288         ((Element)child).setTextContent(now);
289       }
290       else if ("id".equals(child.getLocalName())) { //$NON-NLS-1$
291         Node newNode=createEntry(document);
292         if (debug > 0) {
293           System.out.println(Messages.getString("RSSFeedAddEntryTask.AttachNew") + " <entry/>"); //$NON-NLS-1$ //$NON-NLS-2$
294         }
295         try {
296           if (debug > 0) {
297             System.out.println(SEP); //$NON-NLS-1$
298             transformer.transform(new DOMSource(newNode),new StreamResult(System.out));
299             System.out.println(SEP); //$NON-NLS-1$
300           }
301         }
302         catch (TransformerException e) {
303           e.printStackTrace();
304         }
305         Node refNode=child.getNextSibling();
306         element.insertBefore(document.createTextNode(NL + "  "),refNode); //$NON-NLS-1$
307         element.insertBefore(newNode,refNode);
308         break;
309       }
310     }
311     try {
312       transformer.transform(new DOMSource(document),new StreamResult(new OutputStreamWriter(new FileOutputStream(file))));
313       if (debug > 1) {
314         System.out.println(SEP); //$NON-NLS-1$
315         transformer.transform(new DOMSource(document),new StreamResult(System.out));
316         System.out.println(SEP); //$NON-NLS-1$
317       }
318     }
319     catch (FileNotFoundException e) {
320       e.printStackTrace();
321     }
322     catch (TransformerException e) {
323       e.printStackTrace();
324     }
325   }
326 
327 
createEntry(Document document)328   private Element createEntry(Document document) {
329 
330 //  <entry>
331     Element entry =  document.createElement("entry"); //$NON-NLS-1$
332 
333     String[] txt = { NL + "  ", NL + "    ", NL + "      ", NL + "        ", NL + "          " , NL + "            " }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
334     Element elem = null;
335 
336     String projectVersionString = project + SP + (!isNullString(buildAlias)?  //$NON-NLS-1$
337       (buildAlias.startsWith(branch) ?
338         buildAlias + " (" + buildID + ")" :                   // 2.2.0RC2 (S200605051234) //$NON-NLS-1$ //$NON-NLS-2$
339           buildAlias + " (" + branch + "." + buildID + ")") : // Foobar (2.2.0.S200605051234)  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
340             branch + SP + buildID);                                // 2.2.0.S200605051234 //$NON-NLS-1$
341 
342     doVarSubs();
343 
344 //  <title>[announce] " + project + SP + branch + SP + buildID + " is available</title>
345     elem = document.createElement("title"); //$NON-NLS-1$
346     elem.setTextContent(Messages.getString("RSSFeedAddEntryTask.AnnouncePrefix") + projectVersionString + SP + Messages.getString("RSSFeedAddEntryTask.IsAvailable")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
347     attachNode(document, entry, elem, txt[1]);
348 
349 //  <link href=\"" + buildURL + "\"/>
350     elem = document.createElement("link"); //$NON-NLS-1$
351     elem.setAttribute("href", !isNullString(buildURL) ? buildURL : projectVersionString); //$NON-NLS-1$
352     attachNode(document, entry, elem, txt[1]);
353 
354 //  <id>" + buildURL + "</id>
355     elem = document.createElement("id"); //$NON-NLS-1$
356     elem.setTextContent(!isNullString(buildURL) ? buildURL : projectVersionString);
357     attachNode(document, entry, elem, txt[1]);
358 
359 //  <updated>" + getTimestamp() + "</updated>
360     elem = document.createElement("updated"); //$NON-NLS-1$
361     elem.setTextContent(now);
362     attachNode(document, entry, elem, txt[1]);
363 
364 //  <summary>
365     Element summary = document.createElement("summary"); //$NON-NLS-1$
366     attachNode(document, entry, summary, txt[1]);
367 
368 //  <build callisto="" jars="" type="" href="" xmlns="http://www.eclipse.org/2006/BuildFeed">
369     Element build = document.createElement("build"); //$NON-NLS-1$
370     build.setAttribute("jars", jarSigningStatus); //$NON-NLS-1$
371     build.setAttribute("callisto", callistoStatus); //$NON-NLS-1$
372     build.setAttribute("type", buildType); //$NON-NLS-1$
373     build.setAttribute("xmlns", "http://www.eclipse.org/2006/BuildFeed"); //$NON-NLS-1$ //$NON-NLS-2$
374     if (!isNullString(buildURL)) {
375       build.setAttribute("href",buildURL); //$NON-NLS-1$
376     }
377     attachNode(document, summary, build, txt[2]);
378 
379 //  <update>" + usiteURL + "</update>
380     if (!isNullString(updateManagerURL)) {
381       elem = document.createElement("update"); //$NON-NLS-1$
382       elem.setTextContent(updateManagerURL);
383       attachNode(document, build, elem, txt[3]);
384     }
385 
386 //  <downloads>" + dropsURL + "</downloads>
387     if (!isNullString(downloadsURL)) {
388       elem = document.createElement("downloads"); //$NON-NLS-1$
389       elem.setTextContent(downloadsURL);
390       attachNode(document, build, elem, txt[3]);
391     }
392 
393 //  <releasenotes>" + releaseNotesURL + "</releasenotes>
394     if (!isNullString(releaseNotesURL)) {
395       elem = document.createElement("releasenotes"); //$NON-NLS-1$
396       elem.setTextContent(releaseNotesURL);
397       attachNode(document, build, elem, txt[3]);
398     }
399 
400 //  <releases>
401 //    <release os="" ws="" type=""> + filename + </release>
402     if (releases!=null && releases.length>0) {
403       if (releases.length % 5 != 0) {
404         System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf5") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "releases"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
405       }
406       Element releasesElem = document.createElement("releases"); //$NON-NLS-1$
407       for (int i = 0; i < releases.length; i+=5)
408       {
409         Element release = document.createElement("release"); //$NON-NLS-1$
410         release.setAttribute("os", releases[i]); //$NON-NLS-1$
411         release.setAttribute("ws", releases[i+1]); //$NON-NLS-1$
412         release.setAttribute("arch", releases[i+2]); //$NON-NLS-1$
413         release.setAttribute("type", releases[i+3]); //$NON-NLS-1$
414         release.setTextContent(varSub(releases[i+4]));
415         attachNode(document, releasesElem, release, txt[4]);
416       }
417       attachNode(document, build, releasesElem, txt[3]);
418     }
419 
420 //  <tests>
421     Element tests = document.createElement("tests"); //$NON-NLS-1$
422 
423 //    <test type=\"junit\" href=\"" + JUnitTestURL + "\"/>
424     if (!isNullString(JUnitTestURL)) {
425       Element test = document.createElement("test"); //$NON-NLS-1$
426       test.setAttribute("type", "junit"); //$NON-NLS-1$ //$NON-NLS-2$
427       test.setAttribute("href", JUnitTestURL); //$NON-NLS-1$
428       if (JUnitTestResults!=null && JUnitTestResults.length>0) {
429         if (JUnitTestResults.length % 4 != 0) {
430           System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "JUnitTestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
431         }
432         for (int i = 0; i < JUnitTestResults.length; i+=4)
433         {
434           Element result = document.createElement("result"); //$NON-NLS-1$
435           result.setAttribute("os", JUnitTestResults[i]); //$NON-NLS-1$
436           result.setAttribute("ws", JUnitTestResults[i+1]); //$NON-NLS-1$
437           result.setAttribute("arch", JUnitTestResults[i+2]); //$NON-NLS-1$
438           result.setTextContent(JUnitTestResults[i+3]);
439           attachNode(document, test, result, txt[5]);
440         }
441         // extra space to close containing tag
442         elem.appendChild(document.createTextNode(txt[4]));
443       }
444       attachNode(document, tests, test, txt[4]);
445     }
446 
447 //    <test type=\"performance\" href=\"" + performanceTestURL + "\"/>
448     if (!isNullString(performanceTestURL)) {
449       Element test = document.createElement("test"); //$NON-NLS-1$
450       test.setAttribute("type", "performance"); //$NON-NLS-1$ //$NON-NLS-2$
451       test.setAttribute("href", performanceTestURL); //$NON-NLS-1$
452       if (performanceTestResults!=null && performanceTestResults.length>0) {
453         if (performanceTestResults.length % 4 != 0) {
454           System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "performanceTestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
455         }
456         for (int i = 0; i < performanceTestResults.length; i+=4)
457         {
458           Element result = document.createElement("result"); //$NON-NLS-1$
459           result.setAttribute("os", performanceTestResults[i]); //$NON-NLS-1$
460           result.setAttribute("ws", performanceTestResults[i+1]); //$NON-NLS-1$
461           result.setAttribute("arch", performanceTestResults[i+2]); //$NON-NLS-1$
462           result.setTextContent(performanceTestResults[i+3]);
463           attachNode(document, test, result, txt[5]);
464         }
465         // extra space to close containing tag
466         test.appendChild(document.createTextNode(txt[4]));
467       }
468       attachNode(document, tests, test, txt[4]);
469     }
470 
471 //    <test type=\"performance\" href=\"" + performanceTestURL + "\"/>
472     if (!isNullString(APITestURL)) {
473       Element test = document.createElement("test"); //$NON-NLS-1$
474       test.setAttribute("type", "api"); //$NON-NLS-1$ //$NON-NLS-2$
475       test.setAttribute("href", APITestURL); //$NON-NLS-1$
476       if (APITestResults!=null && APITestResults.length>0) {
477         if (APITestResults.length % 4 != 0) {
478           System.err.println(Messages.getString("RSSFeedAddEntryTask.WrongNumberOfVariables") + SP + Messages.getString("RSSFeedAddEntryTask.MustBeMultipleOf4") + SP + Messages.getString("RSSFeedAddEntryTask.InProperty") + SP + "APITestResults"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
479         }
480         for (int i = 0; i < APITestResults.length; i+=4)
481         {
482           Element result = document.createElement("result"); //$NON-NLS-1$
483           result.setAttribute("os", APITestResults[i]); //$NON-NLS-1$
484           result.setAttribute("ws", APITestResults[i+1]); //$NON-NLS-1$
485           result.setAttribute("arch", APITestResults[i+2]); //$NON-NLS-1$
486           result.setTextContent(APITestResults[i+3]);
487           attachNode(document, tests, result, txt[5]);
488         }
489         // extra space to close containing tag
490         test.appendChild(document.createTextNode(txt[4]));
491       }
492       attachNode(document, tests, test, txt[4]);
493     }
494 
495     attachNode(document, build, tests, txt[3]);
496 
497     if (dependencyURLs!=null && dependencyURLs.length>0) {
498   //  <dependencies>
499   //    <dependency>" + dependencyURL + "</dependency>
500       Element dependencies = document.createElement("dependencies"); //$NON-NLS-1$
501       for (int i = 0; i < dependencyURLs.length; i++)
502       {
503         elem = document.createElement("dependency"); //$NON-NLS-1$
504         elem.setTextContent(dependencyURLs[i]);
505         attachNode(document, dependencies, elem, txt[4]);
506       }
507       attachNode(document, build, dependencies, txt[3]);
508     }
509 
510     return entry;
511   }
512 
513   //$ANALYSIS-IGNORE codereview.java.rules.exceptions.RuleExceptionsSpecificExceptions
attachNode(Document document,Element entry,Element elem,String txt)514   private void attachNode(Document document,Element entry,Element elem,String txt){
515     entry.appendChild(document.createTextNode(txt));
516     entry.appendChild(elem);
517   }
518 
getTimestamp()519   private static String getTimestamp() { // eg., 2006-04-10T20:40:08Z
520     return DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN) + "Z";  //$NON-NLS-1$
521   }
522 
doVarSubs()523   private void doVarSubs()
524   {
525     feedURL = varSub(feedURL);
526     buildURL = varSub(buildURL);
527 
528     releaseNotesURL = varSub(releaseNotesURL);
529     updateManagerURL = varSub(updateManagerURL);
530     downloadsURL = varSub(downloadsURL);
531 
532     JUnitTestURL = varSub(JUnitTestURL);
533     performanceTestURL = varSub(performanceTestURL);
534     APITestURL = varSub(APITestURL);
535   }
536 
createTransformer(String encoding)537   public static Transformer createTransformer(String encoding) throws TransformerException
538   {
539     TransformerFactory transformerFactory = TransformerFactory.newInstance();
540 
541     try
542     {
543       transformerFactory.setAttribute("indent-number", new Integer(2)); //$NON-NLS-1$
544     }
545     catch (IllegalArgumentException exception)
546     {
547     }
548 
549     Transformer transformer = transformerFactory.newTransformer();
550 
551     transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
552     transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
553 
554     // Unless a width is set, there will be only line breaks but no indentation.
555     // The IBM JDK and the Sun JDK don't agree on the property name,
556     // so we set them both.
557     //
558     transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
559     transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
560     if (encoding != null)
561     {
562       transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
563     }
564     return transformer;
565   }
566 
567   /*
568    * variable substitution in URLs - eg., replace %%branch%% and %%buildID%% in buildURL
569    */
varSub(String urlstring)570   private String varSub(String urlstring)
571   {
572     if (!isNullString(urlstring) && urlstring.indexOf("%%")>=0) //$NON-NLS-1$
573     {
574       return urlstring.replaceAll(Messages.getString("RSSFeedAddEntryTask.BranchKeyword"), branch).replaceAll(Messages.getString("RSSFeedAddEntryTask.BuildIDKeyword"), buildID).replaceAll(Messages.getString("RSSFeedAddEntryTask.BuildAliasKeyword"), buildAlias); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
575     }
576     return urlstring;
577   }
578 
isNullString(String str)579   private static boolean isNullString(String str)
580   {
581     return RSSFeedUtil.isNullString(str);
582   }
583 
584 }