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