1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.mrep;
  28 
  29 import java.io.BufferedWriter;
  30 import java.io.File;
  31 import java.io.FileOutputStream;
  32 import java.io.IOException;
  33 import java.io.OutputStreamWriter;
  34 import java.io.Writer;
  35 import java.text.DateFormat;
  36 import java.text.ParseException;
  37 import java.text.SimpleDateFormat;
  38 import java.util.Date;
  39 import java.util.Map;
  40 import java.util.Properties;
  41 import javax.xml.parsers.ParserConfigurationException;
  42 import org.xml.sax.Attributes;
  43 import org.xml.sax.ContentHandler;
  44 import org.xml.sax.SAXException;
  45 import org.xml.sax.ext.LexicalHandler;
  46 import org.xml.sax.helpers.AttributesImpl;
  47 import org.xml.sax.helpers.DefaultHandler;
  48 import com.sun.javatest.report.XMLReportMaker;
  49 import javax.xml.transform.TransformerConfigurationException;
  50 import javax.xml.transform.TransformerFactory;
  51 import javax.xml.transform.sax.SAXTransformerFactory;
  52 import javax.xml.transform.sax.TransformerHandler;
  53 import javax.xml.transform.stream.StreamResult;
  54 import org.xml.sax.InputSource;
  55 import org.xml.sax.XMLReader;
  56 
  57 
  58 
  59 class XMLReportWriter {
  60     public static String XML_CHARSET = "UTF-8";
  61     private final AttributesImpl emptyAttr = new AttributesImpl();
  62     private TransformerHandler ser;
  63 
  64     private Writer fw;
  65 
  66     public XMLReportWriter(File file) throws IOException{
  67         Properties outputProps = new Properties();
  68         outputProps.put("indent", "yes");
  69 
  70         outputProps.put("encoding", XML_CHARSET);
  71 
  72         SAXTransformerFactory stf = (SAXTransformerFactory )TransformerFactory.newInstance();
  73         stf.setAttribute("indent-number", 4);
  74         try {
  75             ser = stf.newTransformerHandler();
  76         } catch (TransformerConfigurationException ex) {
  77             ex.printStackTrace();
  78         }
  79 
  80         ser.getTransformer().setOutputProperties(outputProps);
  81 
  82         fw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream(file), XMLReportMaker.XML_CHARSET));
  83         ser.setResult(new StreamResult(fw));
  84     }
  85 
  86     /**
  87      * Convert date to string in ISO-8601 or xs:dateTime format
  88      *
  89      * @param date
  90      *            Date
  91      * @return ISO-8601 String
  92      */
  93     static String dateToISO8601(Date date) {
  94         DateFormat dfISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
  95         // format in (almost) ISO8601 format
  96         String dateStr = dfISO8601.format(date);
  97         // remap the timezone from 0000 to 00:00 (starts at char 22)
  98         return dateStr.substring(0, 22) + ":" + dateStr.substring(22);
  99     }
 100 
 101     /**
 102      * Convert string in ISO-8601 or xs:dateTime format to date
 103      *
 104      * @param dateStr
 105      *            ISO-8601 String
 106      * @return corresponding date
 107      */
 108     static Date ISO8601toDate(String dateStr) throws ParseException {
 109         DateFormat dfISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
 110         // format in (almost) ISO8601 format
 111         Date date = dfISO8601.parse(dateStr.substring(0, 22)
 112                 + dateStr.substring(23));
 113         // remap the timezone from 0000 to 00:00 (starts at char 22)
 114         return date;
 115     }
 116 
 117     private void sReport() throws SAXException {
 118         AttributesImpl atts = new AttributesImpl();
 119         atts.addAttribute("", "", Scheme.XSI, "String", Scheme.XSI_VAL);
 120         atts.addAttribute("", "", Scheme.SCH_LOC, "String", Scheme.SCH_LOC_VAL);
 121         atts.addAttribute("", "", Scheme.REPORT_FORMST, "String", "v1");
 122         atts.addAttribute("", "", Scheme.REPORT_GENTIME, "String",
 123                 dateToISO8601(new Date()));
 124         ser.startElement("", "", Scheme.REPORT, atts);
 125     }
 126 
 127     public void write(File[] file, Map[] map) throws SAXException,
 128             ParserConfigurationException, IOException {
 129         try {
 130             ser.startDocument();
 131             sReport();
 132             ser.startElement("", "", Scheme.WDS, emptyAttr);
 133             for (int i = 0; i < file.length; i++) {
 134                 XMLReader reader = XMLReportReader.createXMLReader(false);
 135                 reader.setContentHandler(new CopyHandler(ser, true, map[i]));
 136                 reader.parse(new InputSource(file[i].getAbsolutePath()));
 137             }
 138             ser.endElement("", "", Scheme.WDS);
 139             ser.startElement("", "", Scheme.TRS, emptyAttr);
 140             for (int i = 0; i < file.length; i++) {
 141                 XMLReader reader = XMLReportReader.createXMLReader(false);
 142                 reader.setContentHandler(new CopyHandler(ser, false, map[i]));
 143                 reader.parse(new InputSource(file[i].getAbsolutePath()));
 144             }
 145             ser.endElement("", "", Scheme.TRS);
 146             ser.endElement("", "", Scheme.REPORT);
 147             ser.endDocument();
 148         } finally {
 149             fw.close();
 150         }
 151 
 152     }
 153 }
 154 
 155 // This handler just copy one xml to other,
 156 // modifying elements according mapping
 157 
 158 class CopyHandler extends DefaultHandler {
 159 
 160     private ContentHandler ser;
 161     private LexicalHandler lh;
 162 
 163     // are we collect workdirs or not
 164     private final boolean isWorkDir;
 165 
 166     // is it need to write
 167     private boolean needWrite;
 168 
 169     // skipping by conflict resolving
 170     private boolean skipByConflict;
 171 
 172     // id -> new_id mapping
 173     // url -> new_TestDescr mapping
 174     private Map map;
 175 
 176     public CopyHandler(ContentHandler ser, boolean isWorkDir, Map map) {
 177         this.ser = ser;
 178         if (ser instanceof LexicalHandler) {
 179             lh = (LexicalHandler) ser;
 180         }
 181         this.isWorkDir = isWorkDir;
 182         this.needWrite = false;
 183         this.map = map;
 184         this.skipByConflict = false;
 185     }
 186 
 187     public void startElement(String namespaceUri, String localName,
 188             String qName, Attributes attrs) throws SAXException {
 189         if (needWrite == true && skipByConflict == false) {
 190 
 191             if (qName.equals(Scheme.WD)) {
 192                 // for WD WD_ID may be updated
 193                 AttributesImpl newAttrs = new AttributesImpl();
 194                 for (int i = 0; i < attrs.getLength(); i++) {
 195                     if (attrs.getQName(i).equals(Scheme.WD_ID)) {
 196                         String id = String.valueOf(map.get(Integer
 197                                 .valueOf(attrs.getValue(i))));
 198                         newAttrs.addAttribute(attrs.getURI(i), attrs
 199                                 .getLocalName(i), attrs.getQName(i), attrs
 200                                 .getType(i), id);
 201                     } else {
 202                         newAttrs.addAttribute(attrs.getURI(i), attrs
 203                                 .getLocalName(i), attrs.getQName(i), attrs
 204                                 .getType(i), attrs.getValue(i));
 205                     }
 206                 }
 207                 ser.startElement("", "", qName, newAttrs);
 208             } else if (qName.equals(Scheme.TR)) {
 209                 // for TR TR_WDID may be updated
 210                 // or TR may be skipped during conflic resolving
 211                 AttributesImpl newAttrs = new AttributesImpl();
 212                 for (int i = 0; i < attrs.getLength(); i++) {
 213                     if (attrs.getQName(i).equals(Scheme.TR_WDID)) {
 214                         int id = ((TestResultDescr) map.get(attrs
 215                                 .getValue(Scheme.TR_URL))).getID();
 216                         if (id < 0) {
 217                             skipByConflict = true;
 218                             return;
 219                         }
 220                         String idS = String.valueOf(id);
 221                         newAttrs.addAttribute(attrs.getURI(i), attrs
 222                                 .getLocalName(i), attrs.getQName(i), attrs
 223                                 .getType(i), idS);
 224                     } else {
 225                         newAttrs.addAttribute(attrs.getURI(i), attrs
 226                                 .getLocalName(i), attrs.getQName(i), attrs
 227                                 .getType(i), attrs.getValue(i));
 228                     }
 229                 }
 230                 ser.startElement("", "", qName, newAttrs);
 231             } else {
 232                 // by default source attributes are used
 233                 ser.startElement("", "", qName, attrs);
 234             }
 235 
 236         }
 237 
 238         // don't write TRS, WDS
 239         // they are written by XMLReportWriter
 240         // also mode needWrite may be changed
 241         if (isWorkDir == true && qName.equals(Scheme.WDS)) {
 242             needWrite = true;
 243         }
 244         if (isWorkDir == false && qName.equals(Scheme.TRS)) {
 245             needWrite = true;
 246         }
 247     }
 248 
 249     public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
 250         if (this.needWrite == false || this.skipByConflict == true)
 251             return;
 252         // copy only text is really present
 253         if (String.copyValueOf(arg0, arg1, arg2).trim().equals(""))
 254             return;
 255         lh.startCDATA();
 256         ser.characters(arg0, arg1, arg2);
 257         lh.endCDATA();
 258     }
 259 
 260     public void endElement(String arg0, String arg1, String arg2)
 261             throws SAXException {
 262         // don't write TRS, WDS
 263         // they are written by XMLReportWriter
 264         // also mode needWrite may be changed
 265         if (isWorkDir == true && arg2.equals(Scheme.WDS)) {
 266             needWrite = false;
 267         }
 268         if (isWorkDir == false && arg2.equals(Scheme.TRS)) {
 269             needWrite = false;
 270         }
 271         if (needWrite == true) {
 272             if (skipByConflict == false)
 273                 ser.endElement(arg0, arg1, arg2);
 274             else if (arg2.equals(Scheme.TR)) {
 275                 skipByConflict = false;
 276             }
 277         }
 278     }
 279 }