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
130 if (path.length == 0) {
131 this.dataMap.put(file, new FileData(this.destFile, element));
132 }
133
134
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
142
143
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 }