001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.unpack200.bytecode; 020 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026/** 027 * A compressor-defined class file attribute. 028 */ 029public class NewAttribute extends BCIRenumberedAttribute { 030 031 // Bytecode-related value (either a bytecode index or a length) 032 private abstract static class AbstractBcValue { 033 034 int actualValue; 035 036 public void setActualValue(final int value) { 037 this.actualValue = value; 038 } 039 040 } 041 042 private static final class BCIndex extends AbstractBcValue { 043 044 private final int index; 045 046 BCIndex(final int index) { 047 this.index = index; 048 } 049 } 050 051 private static final class BCLength extends AbstractBcValue { 052 053 private final int length; 054 055 BCLength(final int length) { 056 this.length = length; 057 } 058 } 059 060 private static final class BCOffset extends AbstractBcValue { 061 062 private final int offset; 063 private int index; 064 065 BCOffset(final int offset) { 066 this.offset = offset; 067 } 068 069 public void setIndex(final int index) { 070 this.index = index; 071 } 072 073 } 074 075 private final List<Integer> lengths = new ArrayList<>(); 076 077 private final List<Object> body = new ArrayList<>(); 078 079 private ClassConstantPool pool; 080 081 private final int layoutIndex; 082 083 public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) { 084 super(attributeName); 085 this.layoutIndex = layoutIndex; 086 } 087 088 public void addBCIndex(final int length, final int value) { 089 lengths.add(Integer.valueOf(length)); 090 body.add(new BCIndex(value)); 091 } 092 093 public void addBCLength(final int length, final int value) { 094 lengths.add(Integer.valueOf(length)); 095 body.add(new BCLength(value)); 096 } 097 098 public void addBCOffset(final int length, final int value) { 099 lengths.add(Integer.valueOf(length)); 100 body.add(new BCOffset(value)); 101 } 102 103 public void addInteger(final int length, final long value) { 104 lengths.add(Integer.valueOf(length)); 105 body.add(Long.valueOf(value)); 106 } 107 108 public void addToBody(final int length, final Object value) { 109 lengths.add(Integer.valueOf(length)); 110 body.add(value); 111 } 112 113 public int getLayoutIndex() { 114 return layoutIndex; 115 } 116 117 /* 118 * (non-Javadoc) 119 * 120 * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#getLength() 121 */ 122 @Override 123 protected int getLength() { 124 int length = 0; 125 for (final Integer len : lengths) { 126 length += len.intValue(); 127 } 128 return length; 129 } 130 131 @Override 132 protected ClassFileEntry[] getNestedClassFileEntries() { 133 int total = 1; 134 for (final Object element : body) { 135 if (element instanceof ClassFileEntry) { 136 total++; 137 } 138 } 139 final ClassFileEntry[] nested = new ClassFileEntry[total]; 140 nested[0] = getAttributeName(); 141 int i = 1; 142 for (final Object element : body) { 143 if (element instanceof ClassFileEntry) { 144 nested[i] = (ClassFileEntry) element; 145 i++; 146 } 147 } 148 return nested; 149 } 150 151 @Override 152 protected int[] getStartPCs() { 153 // Don't need to return anything here as we've overridden renumber 154 return null; 155 } 156 157 @Override 158 public void renumber(final List<Integer> byteCodeOffsets) { 159 if (!renumbered) { 160 Object previous = null; 161 for (final Object obj : body) { 162 if (obj instanceof BCIndex) { 163 final BCIndex bcIndex = (BCIndex) obj; 164 bcIndex.setActualValue(byteCodeOffsets.get(bcIndex.index).intValue()); 165 } else if (obj instanceof BCOffset) { 166 final BCOffset bcOffset = (BCOffset) obj; 167 if (previous instanceof BCIndex) { 168 final int index = ((BCIndex) previous).index + bcOffset.offset; 169 bcOffset.setIndex(index); 170 bcOffset.setActualValue(byteCodeOffsets.get(index).intValue()); 171 } else if (previous instanceof BCOffset) { 172 final int index = ((BCOffset) previous).index + bcOffset.offset; 173 bcOffset.setIndex(index); 174 bcOffset.setActualValue(byteCodeOffsets.get(index).intValue()); 175 } else { 176 // Not sure if this should be able to happen 177 bcOffset.setActualValue(byteCodeOffsets.get(bcOffset.offset).intValue()); 178 } 179 } else if (obj instanceof BCLength) { 180 // previous must be a BCIndex 181 final BCLength bcLength = (BCLength) obj; 182 final BCIndex prevIndex = (BCIndex) previous; 183 final int index = prevIndex.index + bcLength.length; 184 final int actualLength = byteCodeOffsets.get(index).intValue() - prevIndex.actualValue; 185 bcLength.setActualValue(actualLength); 186 } 187 previous = obj; 188 } 189 renumbered = true; 190 } 191 } 192 193 @Override 194 protected void resolve(final ClassConstantPool pool) { 195 super.resolve(pool); 196 for (final Object element : body) { 197 if (element instanceof ClassFileEntry) { 198 ((ClassFileEntry) element).resolve(pool); 199 } 200 } 201 this.pool = pool; 202 } 203 204 /* 205 * (non-Javadoc) 206 * 207 * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#toString() 208 */ 209 @Override 210 public String toString() { 211 return attributeName.underlyingString(); 212 } 213 214 /* 215 * (non-Javadoc) 216 * 217 * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#writeBody(java.io.DataOutputStream) 218 */ 219 @Override 220 protected void writeBody(final DataOutputStream dos) throws IOException { 221 for (int i = 0; i < lengths.size(); i++) { 222 final int length = lengths.get(i).intValue(); 223 final Object obj = body.get(i); 224 long value = 0; 225 if (obj instanceof Long) { 226 value = ((Long) obj).longValue(); 227 } else if (obj instanceof ClassFileEntry) { 228 value = pool.indexOf((ClassFileEntry) obj); 229 } else if (obj instanceof AbstractBcValue) { 230 value = ((AbstractBcValue) obj).actualValue; 231 } 232 // Write 233 switch (length) { 234 case 1: 235 dos.writeByte((int) value); 236 break; 237 case 2: 238 dos.writeShort((int) value); 239 break; 240 case 4: 241 dos.writeInt((int) value); 242 break; 243 case 8: 244 dos.writeLong(value); 245 break; 246 default: 247 break; 248 } 249 } 250 } 251 252}