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