View Javadoc
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: ConstantUtf8.html 1018313 2017-09-18 09:03:04Z britter $
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
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
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       */
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      */
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 }