1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package net
.multiphasicapps
.tool
.manifest
.writer
;
12 import cc
.squirreljme
.jvm
.manifest
.JavaManifest
;
13 import cc
.squirreljme
.jvm
.manifest
.JavaManifestAttributes
;
14 import cc
.squirreljme
.jvm
.manifest
.JavaManifestException
;
15 import cc
.squirreljme
.jvm
.manifest
.JavaManifestKey
;
16 import java
.io
.ByteArrayInputStream
;
17 import java
.io
.ByteArrayOutputStream
;
18 import java
.io
.IOException
;
19 import java
.io
.InputStream
;
20 import java
.io
.OutputStream
;
21 import java
.io
.OutputStreamWriter
;
22 import java
.io
.Writer
;
23 import java
.util
.AbstractMap
;
24 import java
.util
.LinkedHashMap
;
29 * This is a mutable version of {@link JavaManifest}.
31 * This class is not thread safe.
35 public class MutableJavaManifest
36 extends AbstractMap
<String
, MutableJavaManifestAttributes
>
38 /** The maximum number of columns a manifest may have. */
39 private static final int _COLUMN_LIMIT
=
42 /** Main attributes. */
43 protected final Map
<String
, MutableJavaManifestAttributes
> attributes
=
44 new LinkedHashMap
<>();
47 * This initializes a new empty manifest.
51 public MutableJavaManifest()
53 // Always add a main attribute
54 this.attributes
.put("", new MutableJavaManifestAttributes());
58 * Initializes the mutable manifest using a copy of the data from an
61 * @param __man The immutable manifest.
62 * @throws NullPointerException On null arguments.
65 public MutableJavaManifest(JavaManifest __man
)
66 throws NullPointerException
70 throw new NullPointerException("NARG");
73 for (Map
.Entry
<String
, JavaManifestAttributes
> e
:
76 // Create new attribute set
77 MutableJavaManifestAttributes attr
;
78 this.put(e
.getKey(), (attr
= new MutableJavaManifestAttributes()));
81 for (Map
.Entry
<JavaManifestKey
, String
> f
:
82 e
.getValue().entrySet())
83 attr
.put(f
.getKey(), f
.getValue());
86 // If no main attributes were set then make sure they exist
87 if (!this.containsKey(""))
88 this.put("", new MutableJavaManifestAttributes());
92 * Initializes the mutable manifest using a copy of the data from the
93 * given mutable manifest.
95 * @param __man The mutable manifest to copy from.
96 * @throws NullPointerException On null arguments.
99 public MutableJavaManifest(MutableJavaManifest __man
)
100 throws NullPointerException
104 throw new NullPointerException("NARG");
106 // Go through and add
107 for (Map
.Entry
<String
, MutableJavaManifestAttributes
> e
:
110 // Create new attribute set
111 MutableJavaManifestAttributes attr
;
112 this.put(e
.getKey(), (attr
= new MutableJavaManifestAttributes()));
115 for (Map
.Entry
<JavaManifestKey
, String
> f
:
116 e
.getValue().entrySet())
117 attr
.put(f
.getKey(), f
.getValue());
120 // If no main attributes were set then make sure they exist
121 if (!this.containsKey(""))
122 this.put("", new MutableJavaManifestAttributes());
126 * Builds the specified manifest.
128 * @return The built manifest.
129 * @throws RuntimeException If the manifest could not be built.
132 public final JavaManifest
build()
133 throws RuntimeException
138 try (ByteArrayOutputStream baos
= new ByteArrayOutputStream())
144 bytes
= baos
.toByteArray();
148 try (InputStream is
= new ByteArrayInputStream(bytes
))
150 return new JavaManifest(is
);
154 // {@squirreljme.error AB01 Failed to build the immutable manifest
155 // from the mutable one.}
156 catch (IOException e
)
158 throw new JavaManifestException("AB01", e
);
167 public final Set
<Map
.Entry
<String
, MutableJavaManifestAttributes
>>
170 return this.attributes
.entrySet();
174 * Returns the mapping of main attributes.
176 * @return The main attribute mapping.
179 public final MutableJavaManifestAttributes
getMainAttributes()
189 public final MutableJavaManifestAttributes
put(String __k
,
190 MutableJavaManifestAttributes __v
)
191 throws NullPointerException
194 if (__k
== null || __v
== null)
195 throw new NullPointerException("NARG");
197 // {@squirreljme.error AB02 The specified value is of the wrong
199 if (!(__v
instanceof MutableJavaManifestAttributes
))
200 throw new ClassCastException("AB02");
203 return this.attributes
.put(__k
, __v
);
207 * Writes the manifest data to the given output stream.
209 * @param __os The stream to get the manifest data written to.
210 * @return {@code __os}.
211 * @throws IOException On write errors.
212 * @throws NullPointerException On null arguments.
215 public final OutputStream
write(OutputStream __os
)
216 throws IOException
, NullPointerException
219 throw new NullPointerException("NARG");
221 this.write(new OutputStreamWriter(__os
, "utf-8"));
227 * Writes the manifest data to the given output stream.
229 * @param __os The stream to get the manifest data written to.
230 * @return {@code __os}.
231 * @throws IOException On write errors.
232 * @throws NullPointerException On null arguments.
235 public final Appendable
write(Appendable __os
)
236 throws IOException
, NullPointerException
240 throw new NullPointerException("NARG");
242 // Write main attribute first
243 this.__write(__os
, this.getMainAttributes());
245 // Write other attributes
246 for (Map
.Entry
<String
, MutableJavaManifestAttributes
> e
:
247 this.attributes
.entrySet())
249 // Ignore the main attribute
250 String k
= e
.getKey();
254 // Sub-attributes are always spaced after the previous one
258 this.__write(__os
, "Name", k
);
261 this.__write(__os
, e
.getValue());
264 // Java ME has no flushable so we only know two classes which are
265 if (__os
instanceof OutputStream
)
266 ((OutputStream
)__os
).flush();
267 else if (__os
instanceof Writer
)
268 ((Writer
)__os
).flush();
274 * Writes attributes to the output.
276 * @param __w The stream to write to.
277 * @throws IOException On write errors.
278 * @throws NullPointerException On null arguments.
281 private void __write(Appendable __w
, MutableJavaManifestAttributes __a
)
282 throws IOException
, NullPointerException
285 if (__w
== null || __a
== null)
286 throw new NullPointerException("NARG");
288 // The attribute version is always first
289 JavaManifestKey verk
= new JavaManifestKey("MANIFEST-VERSION");
290 String ver
= __a
.get(verk
);
292 this.__write(__w
, "MANIFEST-VERSION", ver
);
294 this.__write(__w
, "MANIFEST-VERSION", "1.0");
297 for (Map
.Entry
<JavaManifestKey
, String
> e
: __a
.entrySet())
299 // Do not write the version twice
300 JavaManifestKey k
= e
.getKey();
305 this.__write(__w
, k
.inputString(), e
.getValue());
310 * Writes the given key and value to the output.
312 * @param __w The stream to write to.
313 * @param __k The key to write.
314 * @param __v The value to write.
315 * @throws NullPointerException On null arguments.
318 private void __write(Appendable __w
, String __k
, String __v
)
319 throws IOException
, NullPointerException
322 if (__w
== null || __k
== null || __v
== null)
323 throw new NullPointerException("NARG");
327 for (int z
= 0; z
< 2; z
++)
329 String s
= (z
== 0 ? __k
: __v
);
333 for (int i
= 0; i
< n
; i
++)
335 // Ignore out of range characters
336 char c
= s
.charAt(i
);
340 // Would be on a new line?
341 int nextcol
= col
+ 1;
342 boolean newline
= false;
343 if (nextcol
>= MutableJavaManifest
._COLUMN_LIMIT
)
345 // If the current character is a space then it will
346 // be lost on the following line.
352 // Indent next line with space as long as this is not
353 // the last character being written
360 // Write the character, but if a space was written early then
362 if ((c
== ' ' && !newline
) || c
!= ' ')