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