View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.unpack200;
20  
21  import org.apache.commons.compress.harmony.pack200.Codec;
22  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
23  import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
24  import org.apache.commons.lang3.StringUtils;
25  
26  /**
27   * Defines a layout that describes how an attribute will be transmitted.
28   */
29  public class AttributeLayout implements IMatcher {
30  
31      /**
32       * {@value}
33       */
34      public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$
35  
36      /**
37       * {@value}
38       */
39      public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$
40  
41      /**
42       * {@value}
43       */
44      public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$
45  
46      /**
47       * {@value}
48       */
49      public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$
50  
51      /**
52       * {@value}
53       */
54      public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$
55  
56      /**
57       * {@value}
58       */
59      public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$
60  
61      /**
62       * {@value}
63       */
64      public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$
65  
66      /**
67       * {@value}
68       */
69      public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$
70  
71      /**
72       * {@value}
73       */
74      public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$
75  
76      /**
77       * {@value}
78       */
79      public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$
80  
81      /**
82       * {@value}
83       */
84      public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$
85  
86      /**
87       * {@value}
88       */
89      public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$
90  
91      /**
92       * {@value}
93       */
94      public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$
95  
96      /**
97       * {@value}
98       */
99      public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$
100 
101     /**
102      * {@value}
103      */
104     public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$
105 
106     /**
107      * {@value}
108      */
109     public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$
110 
111     /**
112      * {@value}
113      */
114     public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$
115 
116     /**
117      * {@value}
118      */
119     public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$
120 
121     /**
122      * {@value}
123      */
124     public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$
125 
126     /**
127      * {@value}
128      */
129     public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$
130 
131     /**
132      * {@value}
133      */
134     public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$
135 
136     /**
137      * {@value}
138      */
139     public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$
140 
141     /**
142      * {@value}
143      */
144     public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$
145 
146     /**
147      * {@value}
148      */
149     public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$
150 
151     /**
152      * {@value}
153      */
154     public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$
155 
156     /**
157      * {@value}
158      */
159     public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$
160 
161     /**
162      * {@value}
163      */
164     public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$
165 
166     /**
167      * {@value}
168      */
169     public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$
170 
171     /**
172      * {@value}
173      */
174     public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$
175 
176     /**
177      * {@value}
178      */
179     public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$
180 
181     /**
182      * {@value}
183      */
184     public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$
185 
186     /**
187      * {@value}
188      */
189     public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$
190 
191     /**
192      * {@value}
193      */
194     public static final int CONTEXT_CLASS = 0;
195 
196     /**
197      * {@value}
198      */
199     public static final int CONTEXT_CODE = 3;
200 
201     /**
202      * {@value}
203      */
204     public static final int CONTEXT_FIELD = 1;
205 
206     /**
207      * {@value}
208      */
209     public static final int CONTEXT_METHOD = 2;
210 
211     /**
212      * Context names.
213      */
214     public static final String[] contextNames = { "Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
215             "Code", }; //$NON-NLS-1$
216 
217     private static ClassFileEntry getValue(final String layout, long longIndex, final SegmentConstantPool pool) throws Pack200Exception {
218         if (layout.startsWith("R")) { //$NON-NLS-1$
219             // references
220             if (layout.indexOf('N') != -1) {
221                 longIndex--;
222             }
223             if (layout.startsWith("RU")) { //$NON-NLS-1$
224                 return pool.getValue(SegmentConstantPool.UTF_8, longIndex);
225             }
226             if (layout.startsWith("RS")) { //$NON-NLS-1$
227                 return pool.getValue(SegmentConstantPool.SIGNATURE, longIndex);
228             }
229         } else if (layout.startsWith("K")) { //$NON-NLS-1$
230             final char type = layout.charAt(1);
231             switch (type) {
232             case 'S': // String
233                 return pool.getValue(SegmentConstantPool.CP_STRING, longIndex);
234             case 'I': // Int (or byte or short)
235             case 'C': // Char
236                 return pool.getValue(SegmentConstantPool.CP_INT, longIndex);
237             case 'F': // Float
238                 return pool.getValue(SegmentConstantPool.CP_FLOAT, longIndex);
239             case 'J': // Long
240                 return pool.getValue(SegmentConstantPool.CP_LONG, longIndex);
241             case 'D': // Double
242                 return pool.getValue(SegmentConstantPool.CP_DOUBLE, longIndex);
243             }
244         }
245         throw new Pack200Exception("Unknown layout encoding: " + layout);
246     }
247 
248     private final int context;
249 
250     private final int index;
251 
252     private final String layout;
253 
254     private long mask;
255 
256     private final String name;
257     private final boolean isDefault;
258     private int backwardsCallCount;
259 
260     /**
261      * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);})
262      *
263      * @param name    The layout name.
264      * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}.
265      * @param layout  The layout.
266      * @param index   The index, currently used as part of computing the hash code.
267      * @throws Pack200Exception Attribute context out of range.
268      * @throws Pack200Exception Cannot have a null layout.
269      * @throws Pack200Exception Cannot have an unnamed layout.
270      */
271     public AttributeLayout(final String name, final int context, final String layout, final int index) throws Pack200Exception {
272         this(name, context, layout, index, true);
273     }
274 
275     /**
276      * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);})
277      *
278      * @param name    The layout name.
279      * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}.
280      * @param layout  The layout.
281      * @param index   The index, currently used as part of computing the hash code.
282      * @param isDefault Whether this is the default layout.
283      * @throws Pack200Exception Attribute context out of range.
284      * @throws Pack200Exception Cannot have a null layout.
285      * @throws Pack200Exception Cannot have an unnamed layout.
286      */
287     public AttributeLayout(final String name, final int context, final String layout, final int index, final boolean isDefault) throws Pack200Exception {
288         this.index = index;
289         this.context = context;
290         if (index >= 0) {
291             this.mask = 1L << index;
292         } else {
293             this.mask = 0;
294         }
295         if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD && context != CONTEXT_METHOD) {
296             throw new Pack200Exception("Attribute context out of range: " + context);
297         }
298         if (layout == null) {
299             throw new Pack200Exception("Cannot have a null layout");
300         }
301         if (StringUtils.isEmpty(name)) {
302             throw new Pack200Exception("Cannot have an unnamed layout");
303         }
304         this.name = name;
305         this.layout = layout;
306         this.isDefault = isDefault;
307     }
308 
309     /**
310      * Gets the Codec based on the layout.
311      *
312      * @return the Codec.
313      */
314     public Codec getCodec() {
315         if (layout.indexOf('O') >= 0) {
316             return Codec.BRANCH5;
317         }
318         if (layout.indexOf('P') >= 0) {
319             return Codec.BCI5;
320         }
321         if (layout.indexOf('S') >= 0 && !layout.contains("KS") //$NON-NLS-1$
322                 && !layout.contains("RS")) { //$NON-NLS-1$
323             return Codec.SIGNED5;
324         }
325         if (layout.indexOf('B') >= 0) {
326             return Codec.BYTE1;
327         }
328         return Codec.UNSIGNED5;
329     }
330 
331     /**
332      * Gets the context.
333      *
334      * @return the context.
335      */
336     public int getContext() {
337         return context;
338     }
339 
340     /**
341      * Gets the index.
342      *
343      * @return the index.
344      */
345     public int getIndex() {
346         return index;
347     }
348 
349     /**
350      * Gets the layout.
351      *
352      * @return the layout.
353      */
354     public String getLayout() {
355         return layout;
356     }
357 
358     /**
359      * Gets the name.
360      *
361      * @return the name.
362      */
363     public String getName() {
364         return name;
365     }
366 
367     /**
368      * Gets the ClassFileEntry for the given input.
369      *
370      * @param longIndex An index into the segment constant pool.
371      * @param pool the segment constant pool.
372      * @return the matching ClassFileEntry.
373      * @throws Pack200Exception if the input is invalid.
374      */
375     public ClassFileEntry getValue(final long longIndex, final SegmentConstantPool pool) throws Pack200Exception {
376         return getValue(layout, longIndex, pool);
377     }
378 
379     /**
380      * Gets the ClassFileEntry for the given input.
381      *
382      * @param longIndex An index into the segment constant pool.
383      * @param type the Java type signature.
384      * @param pool the segment constant pool.
385      * @return the matching ClassFileEntry.
386      * @throws Pack200Exception if the input is invalid.
387      */
388     public ClassFileEntry getValue(final long longIndex, final String type, final SegmentConstantPool pool) throws Pack200Exception {
389         // TODO This really needs to be better tested, esp. the different types
390         // TODO This should have the ability to deal with RUN stuff too, and
391         // unions
392         if (!layout.startsWith("KQ")) {
393             return getValue(layout, longIndex, pool);
394         }
395         if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
396             return getValue("KS", longIndex, pool);
397         }
398         return getValue("K" + type + layout.substring(2), longIndex, //$NON-NLS-1$
399                 pool);
400     }
401 
402     @Override
403     public int hashCode() {
404         final int prime = 31;
405         int r = 1;
406         if (name != null) {
407             r = r * prime + name.hashCode();
408         }
409         if (layout != null) {
410             r = r * prime + layout.hashCode();
411         }
412         r = r * prime + index;
413         r = r * prime + context;
414         return r;
415     }
416 
417     /**
418      * Tests whether this is the default layout.
419      *
420      * @return whether this is the default layout.
421      */
422     public boolean isDefaultLayout() {
423         return isDefault;
424     }
425 
426     /*
427      * (non-Javadoc)
428      *
429      * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
430      */
431     @Override
432     public boolean matches(final long value) {
433         return (value & mask) != 0;
434     }
435 
436     /**
437      * Gets the backward call count.
438      *
439      * @return the backward call count.
440      */
441     public int numBackwardsCallables() {
442         if ("*".equals(layout)) {
443             return 1;
444         }
445         return backwardsCallCount;
446     }
447 
448     /**
449      * Sets the backward call count.
450      *
451      * @param backwardsCallCount the backward call count.
452      */
453     public void setBackwardsCallCount(final int backwardsCallCount) {
454         this.backwardsCallCount = backwardsCallCount;
455     }
456 
457     @Override
458     public String toString() {
459         return contextNames[context] + ": " + name;
460     }
461 
462 }