/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.spi.commons.util; import java.lang.ref.SoftReference; /** * A few String utility functions. */ public class StringCache { public static final boolean OBJECT_CACHE = getBooleanSetting("jackrabbit.stringCache", true); public static final int OBJECT_CACHE_SIZE = nextPowerOf2(getIntSetting("jackrabbit.stringCacheSize", 1024)); private static SoftReference<String[]> softCache = new SoftReference<String[]>(null); private StringCache() { // utility class } private static int nextPowerOf2(int x) { long i = 1; while (i < x && i < (Integer.MAX_VALUE / 2)) { i += i; } return (int) i; } private static boolean getBooleanSetting(String name, boolean defaultValue) { String s = getProperty(name); if (s != null) { try { return Boolean.valueOf(s).booleanValue(); } catch (NumberFormatException e) { // ignore } } return defaultValue; } private static int getIntSetting(String name, int defaultValue) { String s = getProperty(name); if (s != null) { try { return Integer.decode(s).intValue(); } catch (NumberFormatException e) { // ignore } } return defaultValue; } private static String getProperty(String name) { try { return System.getProperty(name); } catch (Exception e) { // SecurityException // applets may not do that - ignore return null; } } private static String[] getCache() { String[] cache; // softCache can be null due to a Tomcat problem // a workaround is disable the system property org.apache. // catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES if (softCache != null) { cache = softCache.get(); if (cache != null) { return cache; } } try { cache = new String[OBJECT_CACHE_SIZE]; } catch (OutOfMemoryError e) { return null; } softCache = new SoftReference<String[]>(cache); return cache; } /** * Get the string from the cache if possible. If the string has not been * found, it is added to the cache. If there is such a string in the cache, * that one is returned. * * @param s the original string * @return a string with the same content, if possible from the cache */ public static String cache(String s) { if (!OBJECT_CACHE) { return s; } if (s == null) { return s; } else if (s.length() == 0) { return ""; } int hash = s.hashCode(); String[] cache = getCache(); if (cache != null) { int index = hash & (OBJECT_CACHE_SIZE - 1); String cached = cache[index]; if (cached != null) { if (s.equals(cached)) { return cached; } } cache[index] = s; } return s; } /** * Get a string from the cache, and if no such string has been found, create * a new one with only this content. This solves out of memory problems if * the string is a substring of another, large string. In Java, strings are * shared, which could lead to memory problems. This avoid such problems. * * @param s the string * @return a string that is guaranteed not be a substring of a large string */ public static String fromCacheOrNew(String s) { if (!OBJECT_CACHE) { return s; } if (s == null) { return s; } else if (s.length() == 0) { return ""; } int hash = s.hashCode(); String[] cache = getCache(); int index = hash & (OBJECT_CACHE_SIZE - 1); if (cache == null) { return s; } String cached = cache[index]; if (cached != null) { if (s.equals(cached)) { return cached; } } // create a new object that is not shared // (to avoid out of memory if it is a substring of a big String) // NOPMD s = new String(s); cache[index] = s; return s; } /** * Clear the cache. This method is used for testing. */ public static void clearCache() { softCache = new SoftReference<String[]>(null); } }