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}