View Javadoc

1   /***
2    * 
3    */
4   package com.fernsroth.squashfs;
5   
6   import java.io.File;
7   import java.io.FileOutputStream;
8   import java.io.IOException;
9   import java.util.HashMap;
10  import java.util.Map;
11  
12  import javax.xml.parsers.DocumentBuilder;
13  import javax.xml.parsers.DocumentBuilderFactory;
14  import javax.xml.parsers.ParserConfigurationException;
15  import javax.xml.transform.OutputKeys;
16  import javax.xml.transform.Transformer;
17  import javax.xml.transform.TransformerConfigurationException;
18  import javax.xml.transform.TransformerException;
19  import javax.xml.transform.TransformerFactory;
20  import javax.xml.transform.TransformerFactoryConfigurationError;
21  import javax.xml.transform.dom.DOMSource;
22  import javax.xml.transform.stream.StreamResult;
23  
24  import org.w3c.dom.Document;
25  import org.w3c.dom.Element;
26  import org.w3c.dom.Node;
27  
28  import com.fernsroth.squashfs.model.BaseFile;
29  import com.fernsroth.squashfs.model.Directory;
30  import com.fernsroth.squashfs.model.SFSSquashedFile;
31  import com.fernsroth.squashfs.model.SymLink;
32  
33  /***
34   * 
35   * @author Joseph M. Ferner (Near Infinity Corporation)
36   */
37  public class OutputWalkHandler implements WalkHandler {
38  
39      /***
40       * the XML document builder.
41       */
42      private static DocumentBuilder builder;
43  
44      /***
45       * an identity transformer.
46       */
47      private static Transformer identityTransformer;
48  
49      static {
50          try {
51              DocumentBuilderFactory builderFactory = DocumentBuilderFactory
52                      .newInstance();
53              builder = builderFactory.newDocumentBuilder();
54  
55              TransformerFactory transformerFactory = TransformerFactory
56                      .newInstance();
57              identityTransformer = transformerFactory.newTransformer();
58              identityTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
59              identityTransformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
60                      SquashFSGlobals.DOCTYPE_PUBLIC);
61              identityTransformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
62                      SquashFSGlobals.DOCTYPE_SYSTEM);
63          } catch (ParserConfigurationException e) {
64              throw new ExceptionInInitializerError(e);
65          } catch (TransformerConfigurationException e) {
66              throw new ExceptionInInitializerError(e);
67          } catch (TransformerFactoryConfigurationError e) {
68              throw new ExceptionInInitializerError(e);
69          }
70      }
71  
72      /***
73       * the reader.
74       */
75      private SquashFSReader reader;
76  
77      /***
78       * the destination file.
79       */
80      private File destFile;
81  
82      /***
83       * mapping between directories and files.
84       */
85      private Map<BaseFile, FileData> dataMap = new HashMap<BaseFile, FileData>();
86  
87      /***
88       * the manifest xml.
89       */
90      private Document manifest;
91  
92      /***
93       * the top level manifest element.
94       */
95      private Element manifestElement;
96  
97      /***
98       * includes the mtime in the manifest file.
99       */
100     private boolean includeMTimeInManfest = true;
101 
102     /***
103      * @param reader the source file.
104      * @param destFile the destination file.
105      * @throws ParserConfigurationException 
106      */
107     public OutputWalkHandler(SquashFSReader reader, File destFile)
108             throws ParserConfigurationException {
109         this.reader = reader;
110         this.destFile = destFile;
111         this.manifest = builder.newDocument();
112         this.manifestElement = this.manifest.createElement("squashfs-manifest");
113         this.manifest.appendChild(this.manifestElement);
114     }
115 
116     /***
117      * {@inheritDoc}
118      */
119     public void visit(Directory[] path, BaseFile file) throws IOException {
120         Node parent;
121         if (path.length == 0) {
122             parent = this.manifestElement;
123         } else {
124             parent = this.dataMap.get(path[path.length - 1])
125                     .getManifestElement();
126         }
127         Element element = createManifestElement(parent, file);
128 
129         // root
130         if (path.length == 0) {
131             this.dataMap.put(file, new FileData(this.destFile, element));
132         }
133 
134         // non-root
135         else {
136             File parentFile = this.dataMap.get(path[path.length - 1]).getFile();
137             File f = new File(parentFile, fixName(file.getName()));
138             if (file instanceof Directory) {
139                 f.mkdirs();
140             } else if (file instanceof SFSSquashedFile) {
141                 /* enable this to output explicit file attribute to xml manifest file.
142                  element.setAttribute("file", SquashFSUtils.getRelativePath(
143                  this.destFile, f));
144                  */
145                 FileOutputStream outFile = new FileOutputStream(f);
146                 this.reader.writeFile((SFSSquashedFile) file, outFile);
147                 outFile.close();
148             }
149             this.dataMap.put(file, new FileData(f, element));
150         }
151     }
152 
153     /***
154      * fix a name for the platform.
155      * @param name the name to fix.
156      * @return the normalized name.
157      */
158     private String fixName(String name) {
159         return name;
160     }
161 
162     /***
163      * creates a manifest element.
164      * @param parent parent node.
165      * @param file the file to create it from.
166      * @return the manifest element.
167      */
168     private Element createManifestElement(Node parent, BaseFile file) {
169         Element elem;
170         if (file instanceof Directory && parent == this.manifestElement) {
171             elem = this.manifest.createElement("root-directory");
172         } else if (file instanceof Directory) {
173             elem = this.manifest.createElement("directory");
174         } else if (file instanceof SFSSquashedFile) {
175             elem = this.manifest.createElement("file");
176         } else if (file instanceof SymLink) {
177             elem = this.manifest.createElement("symbolic-link");
178         } else {
179             throw new RuntimeException("unknown file type '"
180                     + file.getClass().getName() + "'");
181         }
182 
183         if (file.getName() != null) {
184             elem.setAttribute("name", file.getName());
185         }
186         if (file instanceof SymLink) {
187             elem.setAttribute("link", ((SymLink) file).getLinkName());
188         }
189 
190         if (file.getUid() != 0) {
191             elem.setAttribute("uid", Long.toString(file.getUid()));
192         }
193         if (file.getGuid() != 0) {
194             elem.setAttribute("guid", Long.toString(file.getGuid()));
195         }
196         elem.setAttribute("mode", SquashFSUtils.getModeString(file));
197         if (this.includeMTimeInManfest) {
198             elem.setAttribute("mtime", SquashFSUtils.ISO8601_FORMAT
199                     .format(SquashFSUtils.getDateFromMTime(file.getMTime())));
200         }
201 
202         parent.appendChild(elem);
203         return elem;
204     }
205 
206     /***
207      * writes the manifest file.
208      * @param manifestFile the manifest file to write.
209      * @throws IOException 
210      * @throws TransformerException 
211      */
212     public void writeManifest(File manifestFile) throws IOException,
213             TransformerException {
214         manifestFile.createNewFile();
215 
216         identityTransformer.transform(new DOMSource(this.manifest),
217                 new StreamResult(manifestFile));
218     }
219 
220     /***
221      * contains data about a path.
222      */
223     private class FileData {
224 
225         /***
226          * the file.
227          */
228         private File file;
229 
230         /***
231          * the manifest element. 
232          */
233         private Element element;
234 
235         /***
236          * @param file the file.
237          * @param manifestElement the manifest element.
238          */
239         public FileData(File file, Element manifestElement) {
240             this.file = file;
241             this.element = manifestElement;
242         }
243 
244         /***
245          * @return the file
246          */
247         public File getFile() {
248             return this.file;
249         }
250 
251         /***
252          * @return the manifestElement
253          */
254         public Element getManifestElement() {
255             return this.element;
256         }
257     }
258 
259     /***
260      * @param includeMTimeInManfest
261      */
262     public void setIncludeMTimeInManfest(boolean includeMTimeInManfest) {
263         this.includeMTimeInManfest = includeMTimeInManfest;
264     }
265 }