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 * 017 */ 018package org.apache.bcel.classfile; 019 020import java.io.DataInput; 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.util.HashMap; 024import java.util.LinkedHashMap; 025import java.util.Map; 026 027import org.apache.bcel.Const; 028 029/** 030 * This class is derived from the abstract {@link Constant} 031 * and represents a reference to a Utf8 encoded string. 032 * 033 * @version $Id: ConstantUtf8.java 1750029 2016-06-23 22:14:38Z sebb $ 034 * @see Constant 035 */ 036public final class ConstantUtf8 extends Constant { 037 038 private final String bytes; 039 040 // TODO these should perhaps be AtomicInt? 041 private static volatile int considered = 0; 042 private static volatile int hits = 0; 043 private static volatile int skipped = 0; 044 private static volatile int created = 0; 045 046 // Set the size to 0 or below to skip caching entirely 047 private static final int MAX_CACHED_SIZE = 048 Integer.getInteger("bcel.maxcached.size", 200).intValue();// CHECKSTYLE IGNORE MagicNumber 049 private static final boolean BCEL_STATISTICS = Boolean.getBoolean("bcel.statistics"); 050 051 052 private static class CACHE_HOLDER { 053 054 private static final int MAX_CACHE_ENTRIES = 20000; 055 private static final int INITIAL_CACHE_CAPACITY = (int)(MAX_CACHE_ENTRIES/0.75); 056 057 private static final HashMap<String, ConstantUtf8> CACHE = 058 new LinkedHashMap<String, ConstantUtf8>(INITIAL_CACHE_CAPACITY, 0.75f, true) { 059 private static final long serialVersionUID = -8506975356158971766L; 060 061 @Override 062 protected boolean removeEldestEntry(final Map.Entry<String, ConstantUtf8> eldest) { 063 return size() > MAX_CACHE_ENTRIES; 064 } 065 }; 066 067 } 068 069 // for accesss by test code 070 static void printStats() { 071 System.err.println("Cache hit " + hits + "/" + considered +", " + skipped + " skipped"); 072 System.err.println("Total of " + created + " ConstantUtf8 objects created"); 073 } 074 075 // for accesss by test code 076 static void clearStats() { 077 hits = considered = skipped = created = 0; 078 } 079 080 static { 081 if (BCEL_STATISTICS) { 082 Runtime.getRuntime().addShutdownHook(new Thread() { 083 @Override 084 public void run() { 085 printStats(); 086 } 087 }); 088 } 089 } 090 091 /** 092 * @since 6.0 093 */ 094 public static ConstantUtf8 getCachedInstance(final String s) { 095 if (s.length() > MAX_CACHED_SIZE) { 096 skipped++; 097 return new ConstantUtf8(s); 098 } 099 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 */ 115 public static ConstantUtf8 getInstance(final String s) { 116 return new ConstantUtf8(s); 117 } 118 119 /** 120 * @since 6.0 121 */ 122 public static ConstantUtf8 getInstance (final DataInput input) throws IOException { 123 return getInstance(input.readUTF()); 124 } 125 126 /** 127 * Initialize from another object. 128 */ 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 */ 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 */ 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 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 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 */ 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 199 public final void setBytes( final String bytes ) { 200 throw new UnsupportedOperationException(); 201 } 202 203 204 /** 205 * @return String representation 206 */ 207 @Override 208 public final String toString() { 209 return super.toString() + "(\"" + Utility.replace(bytes, "\n", "\\n") + "\")"; 210 } 211}