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 static abstract class AbstractBcValue { 031 032 int actualValue; 033 034 public void setActualValue(final int value) { 035 this.actualValue = value; 036 } 037 038 } 039 private static final class BCIndex extends AbstractBcValue { 040 041 private final int index; 042 043 public BCIndex(final int index) { 044 this.index = index; 045 } 046 } 047 private static final class BCLength extends AbstractBcValue { 048 049 private final int length; 050 051 public BCLength(final int length) { 052 this.length = length; 053 } 054 } 055 private static final class BCOffset extends AbstractBcValue { 056 057 private final int offset; 058 private int index; 059 060 public BCOffset(final int offset) { 061 this.offset = offset; 062 } 063 064 public void setIndex(final int index) { 065 this.index = index; 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}