1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package io.earcam.instrumental.module.manifest;
20
21 import java.io.OutputStream;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Map.Entry;
26 import java.util.jar.Attributes;
27 import java.util.jar.Attributes.Name;
28 import java.util.jar.Manifest;
29
30 import javax.annotation.WillNotClose;
31
32 import io.earcam.unexceptional.Exceptional;
33
34
35
36
37
38
39
40 public abstract class AbstractManifestBuilder<T extends ManifestInfoBuilder<T>> implements ManifestInfo, ManifestInfoBuilder<T> {
41
42 protected final Map<Name, CharSequence> mainAttributes = new HashMap<>();
43 protected final Map<String, Map<Name, CharSequence>> namedEntries = new HashMap<>();
44
45 protected boolean hooked = false;
46
47
48
49
50
51
52
53
54
55 protected abstract T self();
56
57
58 @Override
59 public boolean equals(Object other)
60 {
61 return other instanceof AbstractManifestBuilder && equals(AbstractManifestBuilder.class.cast(other));
62 }
63
64
65
66
67
68
69
70
71
72
73
74
75 public abstract boolean equals(AbstractManifestBuilder<?> that);
76
77
78
79
80
81
82
83
84
85
86 protected final boolean same(AbstractManifestBuilder<?> that)
87 {
88 return that != null
89 && Objects.equals(that.mainAttributes, mainAttributes)
90 && Objects.equals(that.namedEntries, namedEntries);
91 }
92
93
94 @Override
95 public int hashCode()
96 {
97 return Objects.hash(mainAttributes, namedEntries);
98 }
99
100
101 @Override
102 public T manifestMain(Entry<Name, ? extends CharSequence> attribute)
103 {
104 mainAttributes.put(attribute.getKey(), attribute.getValue());
105 return self();
106 }
107
108
109 @Override
110 public T manifestNamed(ManifestNamedEntry entry)
111 {
112 Map<Name, CharSequence> attributes = namedEntries.computeIfAbsent(entry.name(), n -> new HashMap<>());
113 attributes.putAll(entry.attributes());
114 return self();
115 }
116
117
118 @Override
119 public T mergeFrom(Manifest manifest)
120 {
121 for(Object entry : manifest.getMainAttributes().entrySet()) {
122 @SuppressWarnings("unchecked")
123 Entry<Name, String> x = (Entry<Name, String>) entry;
124 manifestMain(x);
125 }
126
127 for(Map.Entry<String, Attributes> namedEntry : manifest.getEntries().entrySet()) {
128 ManifestNamedEntry x = ManifestNamedEntry.entry(namedEntry.getKey());
129 for(Entry<Object, Object> entry : namedEntry.getValue().entrySet()) {
130 x.attribute((Name) entry.getKey(), (String) entry.getValue());
131 }
132 manifestNamed(x);
133 }
134 return self();
135 }
136
137
138 @Override
139 public Manifest to(Manifest manifest)
140 {
141 hook();
142 mainAttributes.entrySet().forEach(e -> manifest.getMainAttributes().putIfAbsent(e.getKey(), e.getValue().toString()));
143
144 Map<String, Attributes> entries = manifest.getEntries();
145 for(Entry<String, Map<Name, CharSequence>> named : namedEntries.entrySet()) {
146
147 Attributes sink = entries.computeIfAbsent(named.getKey(), n -> new Attributes());
148 for(Entry<Name, CharSequence> source : named.getValue().entrySet()) {
149 sink.put(source.getKey(), source.getValue().toString());
150 }
151 }
152 return manifest;
153 }
154
155
156
157
158
159
160
161 protected final void hook()
162 {
163 if(!hooked) {
164 preBuildHook();
165 hooked = true;
166 preFlightChecks();
167 }
168 }
169
170
171 private void preFlightChecks()
172 {
173 if(!(mainAttributes.containsKey(Name.MANIFEST_VERSION) || mainAttributes.containsKey(Name.SIGNATURE_VERSION))) {
174 mainAttributes.put(Name.MANIFEST_VERSION, "1.0");
175 }
176 }
177
178
179
180
181
182
183
184 protected abstract void preBuildHook();
185
186
187
188
189
190
191
192 protected final void unhook()
193 {
194 hooked = false;
195
196 }
197
198
199 @Override
200 public void to(@WillNotClose OutputStream out)
201 {
202 hook();
203 Exceptional.accept(toManifest()::write, out);
204 }
205
206
207
208
209
210
211
212
213
214 public Map<Name, CharSequence> mainAttributes()
215 {
216 return mainAttributes;
217 }
218
219
220
221
222
223
224
225
226
227 public Map<String, Map<Name, CharSequence>> namedEntries()
228 {
229 return namedEntries;
230 }
231 }