Home | History | Annotate | Download | only in rss
      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
    157   public void setDebug(int debug) { this.debug = debug; }
    158 
    159   //required fields
    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   }
    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   }
    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   }
    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   }
    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   }
    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"
    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
    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
    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
    207   public void setReleaseNotesURL(String releaseNotesURL) { this.releaseNotesURL = releaseNotesURL; }
    208   public void setUpdateManagerURL(String updateManagerURL) { this.updateManagerURL = updateManagerURL; }
    209   public void setDownloadsURL(String downloadsURL) { this.downloadsURL = downloadsURL; }
    210   public void setJarSigningStatus(String jarSigningStatus) { this.jarSigningStatus = jarSigningStatus; }
    211   public void setCallistoStatus(String callistoStatus) { this.callistoStatus = callistoStatus; }
    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
    229   public void setJUnitTestURL(String JUnitTestURL) { this.JUnitTestURL = JUnitTestURL; }
    230   public void setPerformanceTestURL(String performanceTestURL) { this.performanceTestURL = performanceTestURL; }
    231   public void setAPITestURL(String APITestURL) { this.APITestURL = APITestURL; }
    232   public void setJUnitTestResults(String JUnitTestResults) { if (!isNullString(JUnitTestResults)) { this.JUnitTestResults = JUnitTestResults.split(splitter); } }
    233   public void setPerformanceTestResults(String performanceTestResults) { if (!isNullString(performanceTestResults)) { this.performanceTestResults = performanceTestResults.split(splitter); } }
    234   public void setAPITestResults(String APITestResults) { if (!isNullString(APITestResults)) { this.APITestResults = APITestResults.split(splitter); } }
    235 
    236   // The method executing the task
    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
    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 
    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
    514   private void attachNode(Document document,Element entry,Element elem,String txt){
    515     entry.appendChild(document.createTextNode(txt));
    516     entry.appendChild(elem);
    517   }
    518 
    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 
    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 
    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    */
    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 
    579   private static boolean isNullString(String str)
    580   {
    581     return RSSFeedUtil.isNullString(str);
    582   }
    583 
    584 }