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.bytecode;
20  
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  /**
27   * A compressor-defined class file attribute.
28   */
29  public class NewAttribute extends BCIRenumberedAttribute {
30  
31      // Bytecode-related value (either a bytecode index or a length)
32      private abstract static class AbstractBcValue {
33  
34          int actualValue;
35  
36          public void setActualValue(final int value) {
37              this.actualValue = value;
38          }
39  
40      }
41  
42      private static final class BCIndex extends AbstractBcValue {
43  
44          private final int index;
45  
46          BCIndex(final int index) {
47              this.index = index;
48          }
49      }
50  
51      private static final class BCLength extends AbstractBcValue {
52  
53          private final int length;
54  
55          BCLength(final int length) {
56              this.length = length;
57          }
58      }
59  
60      private static final class BCOffset extends AbstractBcValue {
61  
62          private final int offset;
63          private int index;
64  
65          BCOffset(final int offset) {
66              this.offset = offset;
67          }
68  
69          public void setIndex(final int index) {
70              this.index = index;
71          }
72  
73      }
74  
75      private final List<Integer> lengths = new ArrayList<>();
76  
77      private final List<Object> body = new ArrayList<>();
78  
79      private ClassConstantPool pool;
80  
81      private final int layoutIndex;
82  
83      public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) {
84          super(attributeName);
85          this.layoutIndex = layoutIndex;
86      }
87  
88      public void addBCIndex(final int length, final int value) {
89          lengths.add(Integer.valueOf(length));
90          body.add(new BCIndex(value));
91      }
92  
93      public void addBCLength(final int length, final int value) {
94          lengths.add(Integer.valueOf(length));
95          body.add(new BCLength(value));
96      }
97  
98      public void addBCOffset(final int length, final int value) {
99          lengths.add(Integer.valueOf(length));
100         body.add(new BCOffset(value));
101     }
102 
103     public void addInteger(final int length, final long value) {
104         lengths.add(Integer.valueOf(length));
105         body.add(Long.valueOf(value));
106     }
107 
108     public void addToBody(final int length, final Object value) {
109         lengths.add(Integer.valueOf(length));
110         body.add(value);
111     }
112 
113     public int getLayoutIndex() {
114         return layoutIndex;
115     }
116 
117     /*
118      * (non-Javadoc)
119      *
120      * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#getLength()
121      */
122     @Override
123     protected int getLength() {
124         int length = 0;
125         for (final Integer len : lengths) {
126             length += len.intValue();
127         }
128         return length;
129     }
130 
131     @Override
132     protected ClassFileEntry[] getNestedClassFileEntries() {
133         int total = 1;
134         for (final Object element : body) {
135             if (element instanceof ClassFileEntry) {
136                 total++;
137             }
138         }
139         final ClassFileEntry[] nested = new ClassFileEntry[total];
140         nested[0] = getAttributeName();
141         int i = 1;
142         for (final Object element : body) {
143             if (element instanceof ClassFileEntry) {
144                 nested[i] = (ClassFileEntry) element;
145                 i++;
146             }
147         }
148         return nested;
149     }
150 
151     @Override
152     protected int[] getStartPCs() {
153         // Don't need to return anything here as we've overridden renumber
154         return null;
155     }
156 
157     @Override
158     public void renumber(final List<Integer> byteCodeOffsets) {
159         if (!renumbered) {
160             Object previous = null;
161             for (final Object obj : body) {
162                 if (obj instanceof BCIndex) {
163                     final BCIndex bcIndex = (BCIndex) obj;
164                     bcIndex.setActualValue(byteCodeOffsets.get(bcIndex.index).intValue());
165                 } else if (obj instanceof BCOffset) {
166                     final BCOffset bcOffset = (BCOffset) obj;
167                     if (previous instanceof BCIndex) {
168                         final int index = ((BCIndex) previous).index + bcOffset.offset;
169                         bcOffset.setIndex(index);
170                         bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
171                     } else if (previous instanceof BCOffset) {
172                         final int index = ((BCOffset) previous).index + bcOffset.offset;
173                         bcOffset.setIndex(index);
174                         bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
175                     } else {
176                         // Not sure if this should be able to happen
177                         bcOffset.setActualValue(byteCodeOffsets.get(bcOffset.offset).intValue());
178                     }
179                 } else if (obj instanceof BCLength) {
180                     // previous must be a BCIndex
181                     final BCLength bcLength = (BCLength) obj;
182                     final BCIndex prevIndex = (BCIndex) previous;
183                     final int index = prevIndex.index + bcLength.length;
184                     final int actualLength = byteCodeOffsets.get(index).intValue() - prevIndex.actualValue;
185                     bcLength.setActualValue(actualLength);
186                 }
187                 previous = obj;
188             }
189             renumbered = true;
190         }
191     }
192 
193     @Override
194     protected void resolve(final ClassConstantPool pool) {
195         super.resolve(pool);
196         for (final Object element : body) {
197             if (element instanceof ClassFileEntry) {
198                 ((ClassFileEntry) element).resolve(pool);
199             }
200         }
201         this.pool = pool;
202     }
203 
204     /*
205      * (non-Javadoc)
206      *
207      * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#toString()
208      */
209     @Override
210     public String toString() {
211         return attributeName.underlyingString();
212     }
213 
214     /*
215      * (non-Javadoc)
216      *
217      * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#writeBody(java.io.DataOutputStream)
218      */
219     @Override
220     protected void writeBody(final DataOutputStream dos) throws IOException {
221         for (int i = 0; i < lengths.size(); i++) {
222             final int length = lengths.get(i).intValue();
223             final Object obj = body.get(i);
224             long value = 0;
225             if (obj instanceof Long) {
226                 value = ((Long) obj).longValue();
227             } else if (obj instanceof ClassFileEntry) {
228                 value = pool.indexOf((ClassFileEntry) obj);
229             } else if (obj instanceof AbstractBcValue) {
230                 value = ((AbstractBcValue) obj).actualValue;
231             }
232             // Write
233             switch (length) {
234             case 1:
235                 dos.writeByte((int) value);
236                 break;
237             case 2:
238                 dos.writeShort((int) value);
239                 break;
240             case 4:
241                 dos.writeInt((int) value);
242                 break;
243             case 8:
244                 dos.writeLong(value);
245                 break;
246             default:
247                 break;
248             }
249         }
250     }
251 
252 }