1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.classfile; 19 20 import java.io.DataInput; 21 import java.io.DataOutputStream; 22 import java.io.IOException; 23 import java.util.HashMap; 24 import java.util.LinkedHashMap; 25 import java.util.Map; 26 27 import org.apache.bcel.Const; 28 29 /** 30 * This class is derived from the abstract {@link Constant} 31 * and represents a reference to a Utf8 encoded string. 32 * 33 * @version $Id$ 34 * @see Constant 35 */ 36 public final class ConstantUtf8 extends Constant { 37 38 private final String bytes; 39 40 // TODO these should perhaps be AtomicInt? 41 private static volatile int considered = 0; 42 private static volatile int hits = 0; 43 private static volatile int skipped = 0; 44 private static volatile int created = 0; 45 46 // Set the size to 0 or below to skip caching entirely 47 private static final int MAX_CACHED_SIZE = 48 Integer.getInteger("bcel.maxcached.size", 200).intValue();// CHECKSTYLE IGNORE MagicNumber 49 private static final boolean BCEL_STATISTICS = Boolean.getBoolean("bcel.statistics"); 50 51 52 private static class CACHE_HOLDER { 53 54 private static final int MAX_CACHE_ENTRIES = 20000; 55 private static final int INITIAL_CACHE_CAPACITY = (int)(MAX_CACHE_ENTRIES/0.75); 56 57 private static final HashMap<String, ConstantUtf8> CACHE = 58 new LinkedHashMap<String, ConstantUtf8>(INITIAL_CACHE_CAPACITY, 0.75f, true) { 59 private static final long serialVersionUID = -8506975356158971766L; 60 61 @Override 62 protected boolean removeEldestEntry(final Map.Entry<String, ConstantUtf8> eldest) { 63 return size() > MAX_CACHE_ENTRIES; 64 } 65 }; 66 67 } 68 69 // for accesss by test code printStats()70 static void printStats() { 71 System.err.println("Cache hit " + hits + "/" + considered +", " + skipped + " skipped"); 72 System.err.println("Total of " + created + " ConstantUtf8 objects created"); 73 } 74 75 // for accesss by test code clearStats()76 static void clearStats() { 77 hits = considered = skipped = created = 0; 78 } 79 80 static { 81 if (BCEL_STATISTICS) { 82 Runtime.getRuntime().addShutdownHook(new Thread() { 83 @Override 84 public void run() { 85 printStats(); 86 } 87 }); 88 } 89 } 90 91 /** 92 * @since 6.0 93 */ getCachedInstance(final String s)94 public static ConstantUtf8 getCachedInstance(final String s) { 95 if (s.length() > MAX_CACHED_SIZE) { 96 skipped++; 97 return new ConstantUtf8(s); 98 } 99 considered++; 100 synchronized (ConstantUtf8.class) { // might be better with a specific lock object 101 ConstantUtf8 result = CACHE_HOLDER.CACHE.get(s); 102 if (result != null) { 103 hits++; 104 return result; 105 } 106 result = new ConstantUtf8(s); 107 CACHE_HOLDER.CACHE.put(s, result); 108 return result; 109 } 110 } 111 112 /** 113 * @since 6.0 114 */ getInstance(final String s)115 public static ConstantUtf8 getInstance(final String s) { 116 return new ConstantUtf8(s); 117 } 118 119 /** 120 * @since 6.0 121 */ getInstance(final DataInput input)122 public static ConstantUtf8 getInstance (final DataInput input) throws IOException { 123 return getInstance(input.readUTF()); 124 } 125 126 /** 127 * Initialize from another object. 128 */ ConstantUtf8(final ConstantUtf8 c)129 public ConstantUtf8(final ConstantUtf8 c) { 130 this(c.getBytes()); 131 } 132 133 134 /** 135 * Initialize instance from file data. 136 * 137 * @param file Input stream 138 * @throws IOException 139 */ ConstantUtf8(final DataInput file)140 ConstantUtf8(final DataInput file) throws IOException { 141 super(Const.CONSTANT_Utf8); 142 bytes = file.readUTF(); 143 created++; 144 } 145 146 147 /** 148 * @param bytes Data 149 */ ConstantUtf8(final String bytes)150 public ConstantUtf8(final String bytes) { 151 super(Const.CONSTANT_Utf8); 152 if (bytes == null) { 153 throw new IllegalArgumentException("bytes must not be null!"); 154 } 155 this.bytes = bytes; 156 created++; 157 } 158 159 160 /** 161 * Called by objects that are traversing the nodes of the tree implicitely 162 * defined by the contents of a Java class. I.e., the hierarchy of methods, 163 * fields, attributes, etc. spawns a tree of objects. 164 * 165 * @param v Visitor object 166 */ 167 @Override accept( final Visitor v )168 public void accept( final Visitor v ) { 169 v.visitConstantUtf8(this); 170 } 171 172 173 /** 174 * Dump String in Utf8 format to file stream. 175 * 176 * @param file Output file stream 177 * @throws IOException 178 */ 179 @Override dump( final DataOutputStream file )180 public final void dump( final DataOutputStream file ) throws IOException { 181 file.writeByte(super.getTag()); 182 file.writeUTF(bytes); 183 } 184 185 186 /** 187 * @return Data converted to string. 188 */ getBytes()189 public final String getBytes() { 190 return bytes; 191 } 192 193 194 /** 195 * @param bytes the raw bytes of this Utf-8 196 * @deprecated (since 6.0) 197 */ 198 @java.lang.Deprecated setBytes( final String bytes )199 public final void setBytes( final String bytes ) { 200 throw new UnsupportedOperationException(); 201 } 202 203 204 /** 205 * @return String representation 206 */ 207 @Override toString()208 public final String toString() { 209 return super.toString() + "(\"" + Utility.replace(bytes, "\n", "\\n") + "\")"; 210 } 211 } 212