24 */ |
24 */ |
25 |
25 |
26 package jdk.jpackage.internal; |
26 package jdk.jpackage.internal; |
27 |
27 |
28 import java.io.*; |
28 import java.io.*; |
|
29 import java.lang.reflect.InvocationHandler; |
|
30 import java.lang.reflect.Method; |
|
31 import java.lang.reflect.Proxy; |
29 import java.nio.channels.FileChannel; |
32 import java.nio.channels.FileChannel; |
30 import java.nio.file.FileVisitResult; |
33 import java.nio.file.FileVisitResult; |
31 import java.nio.file.Files; |
34 import java.nio.file.Files; |
32 import java.nio.file.Path; |
35 import java.nio.file.Path; |
33 import java.nio.file.SimpleFileVisitor; |
36 import java.nio.file.SimpleFileVisitor; |
34 import java.nio.file.attribute.BasicFileAttributes; |
37 import java.nio.file.attribute.BasicFileAttributes; |
35 import java.util.ArrayList; |
38 import java.util.*; |
36 import java.util.List; |
39 import javax.xml.stream.XMLOutputFactory; |
|
40 import javax.xml.stream.XMLStreamException; |
|
41 import javax.xml.stream.XMLStreamWriter; |
37 |
42 |
38 /** |
43 /** |
39 * IOUtils |
44 * IOUtils |
40 * |
45 * |
41 * A collection of static utility methods. |
46 * A collection of static utility methods. |
219 if (!file.canWrite()) { |
224 if (!file.canWrite()) { |
220 throw new PackagerException("error.cannot-write-to-output-dir", |
225 throw new PackagerException("error.cannot-write-to-output-dir", |
221 file.getAbsolutePath()); |
226 file.getAbsolutePath()); |
222 } |
227 } |
223 } |
228 } |
|
229 |
|
230 public static Path replaceSuffix(Path path, String suffix) { |
|
231 Path parent = path.getParent(); |
|
232 String filename = path.getFileName().toString().replaceAll("\\.[^.]*$", "") |
|
233 + Optional.ofNullable(suffix).orElse(""); |
|
234 return parent != null ? parent.resolve(filename) : Path.of(filename); |
|
235 } |
|
236 |
|
237 public static Path addSuffix(Path path, String suffix) { |
|
238 Path parent = path.getParent(); |
|
239 String filename = path.getFileName().toString() + suffix; |
|
240 return parent != null ? parent.resolve(filename) : Path.of(filename); |
|
241 } |
|
242 |
|
243 @FunctionalInterface |
|
244 public static interface XmlConsumer { |
|
245 void accept(XMLStreamWriter xml) throws IOException, XMLStreamException; |
|
246 } |
|
247 |
|
248 public static void createXml(Path dstFile, XmlConsumer xmlConsumer) throws |
|
249 IOException { |
|
250 XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance(); |
|
251 try (Writer w = new BufferedWriter(new FileWriter(dstFile.toFile()))) { |
|
252 // Wrap with pretty print proxy |
|
253 XMLStreamWriter xml = (XMLStreamWriter) Proxy.newProxyInstance( |
|
254 XMLStreamWriter.class.getClassLoader(), new Class<?>[]{ |
|
255 XMLStreamWriter.class}, new PrettyPrintHandler( |
|
256 xmlFactory.createXMLStreamWriter(w))); |
|
257 |
|
258 xml.writeStartDocument(); |
|
259 xmlConsumer.accept(xml); |
|
260 xml.writeEndDocument(); |
|
261 xml.flush(); |
|
262 xml.close(); |
|
263 } catch (XMLStreamException ex) { |
|
264 throw new IOException(ex); |
|
265 } catch (IOException ex) { |
|
266 throw ex; |
|
267 } |
|
268 } |
|
269 |
|
270 private static class PrettyPrintHandler implements InvocationHandler { |
|
271 |
|
272 PrettyPrintHandler(XMLStreamWriter target) { |
|
273 this.target = target; |
|
274 } |
|
275 |
|
276 @Override |
|
277 public Object invoke(Object proxy, Method method, Object[] args) throws |
|
278 Throwable { |
|
279 switch (method.getName()) { |
|
280 case "writeStartElement": |
|
281 // update state of parent node |
|
282 if (depth > 0) { |
|
283 hasChildElement.put(depth - 1, true); |
|
284 } |
|
285 // reset state of current node |
|
286 hasChildElement.put(depth, false); |
|
287 // indent for current depth |
|
288 target.writeCharacters(EOL); |
|
289 target.writeCharacters(repeat(depth, INDENT)); |
|
290 depth++; |
|
291 break; |
|
292 case "writeEndElement": |
|
293 depth--; |
|
294 if (hasChildElement.get(depth) == true) { |
|
295 target.writeCharacters(EOL); |
|
296 target.writeCharacters(repeat(depth, INDENT)); |
|
297 } |
|
298 break; |
|
299 case "writeProcessingInstruction": |
|
300 case "writeEmptyElement": |
|
301 // update state of parent node |
|
302 if (depth > 0) { |
|
303 hasChildElement.put(depth - 1, true); |
|
304 } |
|
305 // indent for current depth |
|
306 target.writeCharacters(EOL); |
|
307 target.writeCharacters(repeat(depth, INDENT)); |
|
308 break; |
|
309 default: |
|
310 break; |
|
311 } |
|
312 method.invoke(target, args); |
|
313 return null; |
|
314 } |
|
315 |
|
316 private static String repeat(int d, String s) { |
|
317 StringBuilder sb = new StringBuilder(); |
|
318 while (d-- > 0) { |
|
319 sb.append(s); |
|
320 } |
|
321 return sb.toString(); |
|
322 } |
|
323 |
|
324 private final XMLStreamWriter target; |
|
325 private int depth = 0; |
|
326 private final Map<Integer, Boolean> hasChildElement = new HashMap<>(); |
|
327 private static final String INDENT = " "; |
|
328 private static final String EOL = "\n"; |
|
329 } |
224 } |
330 } |