001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.doctool;
017:
018: import org.w3c.dom.Document;
019: import org.w3c.dom.Node;
020: import org.xml.sax.SAXException;
021:
022: import java.io.BufferedReader;
023: import java.io.File;
024: import java.io.FileReader;
025: import java.io.FileWriter;
026: import java.io.IOException;
027: import java.io.PrintWriter;
028:
029: import javax.xml.parsers.DocumentBuilder;
030: import javax.xml.parsers.DocumentBuilderFactory;
031: import javax.xml.parsers.ParserConfigurationException;
032: import javax.xml.transform.Transformer;
033: import javax.xml.transform.TransformerException;
034: import javax.xml.transform.TransformerFactory;
035: import javax.xml.transform.dom.DOMSource;
036: import javax.xml.transform.stream.StreamResult;
037:
038: /**
039: * Takes an input stream and splits it into multiple files. A new file begins
040: * when a line in the input stream begins with a specific prefix followed by
041: * whitespace then an absolute or relative file name to create.
042: */
043: public class SplitterJoiner {
044:
045: public static void main(String[] args) throws IOException {
046: if (args.length < 2) {
047: help();
048: return;
049: } else if (args[0].equals("split")) {
050: String[] files = new String[args.length - 1];
051: System.arraycopy(args, 1, files, 0, args.length - 1);
052: split(files);
053: } else if (args[0].equals("join")) {
054: if (args.length < 4) {
055: help();
056: return;
057: }
058: String[] files = new String[args.length - 3];
059: System.arraycopy(args, 3, files, 0, args.length - 3);
060: merge(args[1], args[2], files);
061: } else {
062: if (!args[0].equals("-h") && !args[0].equals("-?")) {
063: System.err.println("Error: don't know '" + args[0]
064: + "'");
065: }
066: help();
067: return;
068: }
069: }
070:
071: private static void emitFile(PrintWriter out, File outputFile,
072: File inputFile) throws IOException,
073: ParserConfigurationException, SAXException,
074: TransformerException {
075: if (!inputFile.exists()) {
076: System.err.println("Error: Cannot find input file "
077: + inputFile.getPath());
078: return;
079: }
080:
081: if (inputFile.getCanonicalFile().equals(outputFile)) {
082: // skip
083: return;
084: }
085:
086: DocumentBuilderFactory factory = DocumentBuilderFactory
087: .newInstance();
088: DocumentBuilder builder = factory.newDocumentBuilder();
089: Document doc = builder.parse(inputFile);
090: writeTopLevelChildren(doc, out);
091: }
092:
093: private static void help() {
094: System.out.println("Usage: SplitterJoiner split infile+");
095: System.out
096: .println("Usage: SplitterJoiner join tag outfile (infile|dir)+");
097: System.out
098: .println("\tsplit indicates that inputs file should be split into multiple output files");
099: System.out
100: .println("\tjoin indicates xml files (or directories) should be merged into one big xml file (on stdout)");
101: System.out
102: .println("\ttag when joining, the outermost xml tag name");
103: System.out
104: .println("\toutfile when joining, the file to write the joined output into");
105: System.out
106: .println("\tinfile if splitting, an input file to split");
107: System.out
108: .println("\t if joining, a file whose contents should be merged in");
109: System.out
110: .println("\tdir when joining, a directory whose xml files' contents should be merged in");
111: }
112:
113: private static boolean isNewerThan(File file, long lastModified) {
114: if (file.isFile()) {
115: return file.lastModified() > lastModified;
116: }
117:
118: File[] children = file.listFiles();
119: if (children != null) {
120: for (int i = 0, n = children.length; i < n; ++i) {
121: File child = children[i];
122: if (isNewerThan(child, lastModified)) {
123: return true;
124: }
125: }
126: }
127:
128: return false;
129: }
130:
131: private static void merge(String tag, String outputPath,
132: String files[]) {
133: File outputFile = null;
134: try {
135: outputFile = new File(outputPath).getCanonicalFile();
136: } catch (IOException e) {
137: e.printStackTrace();
138: return;
139: }
140:
141: // Maybe we don't need to do anything.
142: //
143: boolean skipMerge = true;
144: if (!outputFile.exists()) {
145: skipMerge = false;
146: } else {
147: long outputFileLastModified = outputFile.lastModified();
148: for (int i = 0, n = files.length; i < n; ++i) {
149: if (isNewerThan(new File(files[i]),
150: outputFileLastModified)) {
151: skipMerge = false;
152: break;
153: }
154: }
155: }
156:
157: if (skipMerge) {
158: // Nothing to do.
159: //
160: return;
161: }
162:
163: try {
164: PrintWriter out = new PrintWriter(
165: new FileWriter(outputFile), true);
166:
167: out.println("<?xml version='1.0'?>");
168: out.println("<" + tag + ">");
169:
170: for (int i = 0; i < files.length; i++) {
171: File file = new File(files[i]);
172: if (file.isFile()) {
173: emitFile(out, outputFile, file);
174: } else {
175: File[] children = file.listFiles();
176: if (children != null) {
177: for (int j = 0; j < children.length; ++j) {
178: if (children[j].isFile()
179: && children[j].getPath().endsWith(
180: ".xml")) {
181: emitFile(out, outputFile, children[j]);
182: }
183: }
184: }
185: }
186: }
187: out.println("</" + tag + ">");
188: out.close();
189: } catch (IOException e) {
190: outputFile.deleteOnExit();
191: e.printStackTrace();
192: } catch (ParserConfigurationException e) {
193: outputFile.deleteOnExit();
194: e.printStackTrace();
195: } catch (SAXException e) {
196: outputFile.deleteOnExit();
197: e.printStackTrace();
198: } catch (TransformerException e) {
199: e.printStackTrace();
200: }
201: }
202:
203: private static void split(String[] files) throws IOException {
204: BufferedReader reader = null;
205: String prefix = null;
206: File inputFile = null;
207:
208: for (int i = 0; i < files.length; i++) {
209:
210: // Close the current reader, if any.
211: //
212: if (reader != null) {
213: reader.close();
214: }
215:
216: // Open the next reader.
217: //
218: String file = files[i];
219: inputFile = new File(file);
220: if (!inputFile.exists()) {
221: System.err.println("Error: Cannot find input file "
222: + inputFile.getPath());
223: return;
224: }
225: reader = new BufferedReader(new FileReader(inputFile));
226:
227: // Parse the input
228: //
229: File outFile = null;
230: PrintWriter writer = null;
231: String line = reader.readLine();
232: while (line != null) {
233: if (prefix == null) {
234: // Learn the prefix.
235: //
236: prefix = line.trim();
237: if (prefix.length() == 0) {
238: // The first line with anything on it counts as the prefix.
239: //
240: prefix = null;
241: }
242: } else if (line.startsWith(prefix)) {
243: // Close the current writer.
244: //
245: if (writer != null) {
246: writer.close();
247: }
248:
249: // Create the next writer.
250: //
251: String outPath = line.substring(prefix.length())
252: .trim();
253: outFile = new File(outPath);
254: if (!outFile.isAbsolute()) {
255: // Make the created file relative to the input file.
256: //
257: File absoluteParentDir = inputFile
258: .getCanonicalFile().getParentFile();
259: outFile = new File(absoluteParentDir, outPath);
260: outFile.getParentFile().mkdirs();
261: }
262:
263: writer = new PrintWriter(new FileWriter(outFile),
264: true);
265:
266: writer
267: .println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
268:
269: } else if (writer != null) {
270: // Write this line to the current file.
271: //
272: writer.println(line);
273: } else {
274: // Ignored -- haven't yet seen a starting prefix.
275: //
276: }
277:
278: line = reader.readLine();
279: }
280:
281: if (writer != null) {
282: writer.close();
283: }
284: }
285: }
286:
287: private static void writeTopLevelChildren(Document doc,
288: PrintWriter out) throws TransformerException {
289: StreamResult result = new StreamResult(out);
290: TransformerFactory transformerFactory = TransformerFactory
291: .newInstance();
292: Transformer transformer = transformerFactory.newTransformer();
293: transformer.setOutputProperty(
294: javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION,
295: "yes");
296: transformer.setOutputProperty(
297: javax.xml.transform.OutputKeys.INDENT, "yes");
298: transformer.setOutputProperty(
299: "{http://xml.apache.org/xslt}indent-amount", "4");
300:
301: Node child = doc.getDocumentElement().getFirstChild();
302: while (child != null) {
303: DOMSource domSource = new DOMSource(child);
304: transformer.transform(domSource, result);
305: child = child.getNextSibling();
306: }
307: }
308:
309: }
|