001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200.bytecode;
020
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.List;
025
026/**
027 * A compressor-defined class file attribute.
028 */
029public class NewAttribute extends BCIRenumberedAttribute {
030
031    // Bytecode-related value (either a bytecode index or a length)
032    private abstract static class AbstractBcValue {
033
034        int actualValue;
035
036        public void setActualValue(final int value) {
037            this.actualValue = value;
038        }
039
040    }
041
042    private static final class BCIndex extends AbstractBcValue {
043
044        private final int index;
045
046        BCIndex(final int index) {
047            this.index = index;
048        }
049    }
050
051    private static final class BCLength extends AbstractBcValue {
052
053        private final int length;
054
055        BCLength(final int length) {
056            this.length = length;
057        }
058    }
059
060    private static final class BCOffset extends AbstractBcValue {
061
062        private final int offset;
063        private int index;
064
065        BCOffset(final int offset) {
066            this.offset = offset;
067        }
068
069        public void setIndex(final int index) {
070            this.index = index;
071        }
072
073    }
074
075    private final List<Integer> lengths = new ArrayList<>();
076
077    private final List<Object> body = new ArrayList<>();
078
079    private ClassConstantPool pool;
080
081    private final int layoutIndex;
082
083    public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) {
084        super(attributeName);
085        this.layoutIndex = layoutIndex;
086    }
087
088    public void addBCIndex(final int length, final int value) {
089        lengths.add(Integer.valueOf(length));
090        body.add(new BCIndex(value));
091    }
092
093    public void addBCLength(final int length, final int value) {
094        lengths.add(Integer.valueOf(length));
095        body.add(new BCLength(value));
096    }
097
098    public void addBCOffset(final int length, final int value) {
099        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}