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    private static class BCIndex extends AbstractBcValue {
030
031        private final int index;
032
033        public BCIndex(final int index) {
034            this.index = index;
035        }
036    }
037    private static class BCLength extends AbstractBcValue {
038
039        private final int length;
040
041        public BCLength(final int length) {
042            this.length = length;
043        }
044    }
045    private static class BCOffset extends AbstractBcValue {
046
047        private final int offset;
048        private int index;
049
050        public BCOffset(final int offset) {
051            this.offset = offset;
052        }
053
054        public void setIndex(final int index) {
055            this.index = index;
056        }
057
058    }
059    // Bytecode-related value (either a bytecode index or a length)
060    private static abstract class AbstractBcValue {
061
062        int actualValue;
063
064        public void setActualValue(final int value) {
065            this.actualValue = value;
066        }
067
068    }
069
070    private final List<Integer> lengths = new ArrayList<>();
071
072    private final List<Object> body = new ArrayList<>();
073
074    private ClassConstantPool pool;
075
076    private final int layoutIndex;
077
078    public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) {
079        super(attributeName);
080        this.layoutIndex = layoutIndex;
081    }
082
083    public void addBCIndex(final int length, final int value) {
084        lengths.add(Integer.valueOf(length));
085        body.add(new BCIndex(value));
086    }
087
088    public void addBCLength(final int length, final int value) {
089        lengths.add(Integer.valueOf(length));
090        body.add(new BCLength(value));
091    }
092
093    public void addBCOffset(final int length, final int value) {
094        lengths.add(Integer.valueOf(length));
095        body.add(new BCOffset(value));
096    }
097
098    public void addInteger(final int length, final long value) {
099        lengths.add(Integer.valueOf(length));
100        body.add(Long.valueOf(value));
101    }
102
103    public void addToBody(final int length, final Object value) {
104        lengths.add(Integer.valueOf(length));
105        body.add(value);
106    }
107
108    public int getLayoutIndex() {
109        return layoutIndex;
110    }
111
112    /*
113     * (non-Javadoc)
114     *
115     * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#getLength()
116     */
117    @Override
118    protected int getLength() {
119        int length = 0;
120        for (final Integer len : lengths) {
121            length += len.intValue();
122        }
123        return length;
124    }
125
126    @Override
127    protected ClassFileEntry[] getNestedClassFileEntries() {
128        int total = 1;
129        for (final Object element : body) {
130            if (element instanceof ClassFileEntry) {
131                total++;
132            }
133        }
134        final ClassFileEntry[] nested = new ClassFileEntry[total];
135        nested[0] = getAttributeName();
136        int i = 1;
137        for (final Object element : body) {
138            if (element instanceof ClassFileEntry) {
139                nested[i] = (ClassFileEntry) element;
140                i++;
141            }
142        }
143        return nested;
144    }
145
146    @Override
147    protected int[] getStartPCs() {
148        // Don't need to return anything here as we've overridden renumber
149        return null;
150    }
151
152    @Override
153    public void renumber(final List<Integer> byteCodeOffsets) {
154        if (!renumbered) {
155            Object previous = null;
156            for (final Object obj : body) {
157                if (obj instanceof BCIndex) {
158                    final BCIndex bcIndex = (BCIndex) obj;
159                    bcIndex.setActualValue(byteCodeOffsets.get(bcIndex.index).intValue());
160                } else if (obj instanceof BCOffset) {
161                    final BCOffset bcOffset = (BCOffset) obj;
162                    if (previous instanceof BCIndex) {
163                        final int index = ((BCIndex) previous).index + bcOffset.offset;
164                        bcOffset.setIndex(index);
165                        bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
166                    } else if (previous instanceof BCOffset) {
167                        final int index = ((BCOffset) previous).index + bcOffset.offset;
168                        bcOffset.setIndex(index);
169                        bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
170                    } else {
171                        // Not sure if this should be able to happen
172                        bcOffset.setActualValue(byteCodeOffsets.get(bcOffset.offset).intValue());
173                    }
174                } else if (obj instanceof BCLength) {
175                    // previous must be a BCIndex
176                    final BCLength bcLength = (BCLength) obj;
177                    final BCIndex prevIndex = (BCIndex) previous;
178                    final int index = prevIndex.index + bcLength.length;
179                    final int actualLength = byteCodeOffsets.get(index).intValue() - prevIndex.actualValue;
180                    bcLength.setActualValue(actualLength);
181                }
182                previous = obj;
183            }
184            renumbered = true;
185        }
186    }
187
188    @Override
189    protected void resolve(final ClassConstantPool pool) {
190        super.resolve(pool);
191        for (final Object element : body) {
192            if (element instanceof ClassFileEntry) {
193                ((ClassFileEntry) element).resolve(pool);
194            }
195        }
196        this.pool = pool;
197    }
198
199    /*
200     * (non-Javadoc)
201     *
202     * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#toString()
203     */
204    @Override
205    public String toString() {
206        return attributeName.underlyingString();
207    }
208
209    /*
210     * (non-Javadoc)
211     *
212     * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#writeBody(java.io.DataOutputStream)
213     */
214    @Override
215    protected void writeBody(final DataOutputStream dos) throws IOException {
216        for (int i = 0; i < lengths.size(); i++) {
217            final int length = lengths.get(i).intValue();
218            final Object obj = body.get(i);
219            long value = 0;
220            if (obj instanceof Long) {
221                value = ((Long) obj).longValue();
222            } else if (obj instanceof ClassFileEntry) {
223                value = pool.indexOf(((ClassFileEntry) obj));
224            } else if (obj instanceof AbstractBcValue) {
225                value = ((AbstractBcValue) obj).actualValue;
226            }
227            // Write
228            switch (length) {
229            case 1:
230                dos.writeByte((int) value);
231                break;
232            case 2:
233                dos.writeShort((int) value);
234                break;
235            case 4:
236                dos.writeInt((int) value);
237                break;
238            case 8:
239                dos.writeLong(value);
240                break;
241            default:
242                break;
243            }
244        }
245    }
246
247}