/******************************************************************************* * Copyright © 2005, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.mof.egl.utils; public class InternUtil { private static final int NUM_COMPONENTS = 10; // the typical maximum number of components in a string array private static final int NUM_STRINGS = 5000; private static final int NUM_STRINGARRAYS = 250; private static StringArrayInternUtil[] STRINGARRAYS = new StringArrayInternUtil[NUM_COMPONENTS]; private static StringInternUtil STRINGS = new StringInternUtil(NUM_STRINGS); public static final String[] EMPTY_STRING_ARRAY = new String[0]; static { for(int i = 0; i < STRINGARRAYS.length; i++) { STRINGARRAYS[i] = new StringArrayInternUtil(NUM_STRINGARRAYS); } } public static String intern(String string) { return STRINGS.intern(string); } public static String[] intern(String[] strings) { return STRINGARRAYS[strings.length > NUM_COMPONENTS - 1 ? 0 : strings.length].intern(strings); } public static String internCaseSensitive(String identifier) { return identifier.intern(); } } class StringArrayInternUtil { private String[][] stringArrays; private int size; private int capacity; private int threshold; public StringArrayInternUtil(int size) { // Protect against small tables if(size < 37) size = 37; this.stringArrays = new String[(int) (size * 1.5f)][]; this.capacity = stringArrays.length; this.threshold = size; } public String[] intern(String[] stringArray) { // Guard against empty string arrays if(stringArray.length == 0) { return InternUtil.EMPTY_STRING_ARRAY; } // Compute the index into the table int index = hashCode(stringArray) % capacity; // Linear probe in the table, if found return the existing entry String[] probe; while((probe = stringArrays[index]) != null) { if(equalsIgnoreCase(stringArray, probe)) return probe; if(++index == capacity) index = 0; } // Intern components of string array before putting into the table for(int i = 0; i < stringArray.length; i++) { stringArray[i] = InternUtil.intern(stringArray[i]); } stringArrays[index] = stringArray; // Rehash table if threshold exceeded if(++size == threshold) rehash(); // Return the just inserted component-interned string arrays return stringArray; } private int hashCode(String[] stringArray) { // Use the case-insensitive hashcode of the last component as the hash code of the entire array String lastComponent = stringArray[stringArray.length - 1]; int result = 0; char[] charArray = lastComponent.toCharArray(); for(int i = 0; i < charArray.length; i++) { char current = charArray[i]; result = 31 * result + Character.toLowerCase(Character.toUpperCase(current)); } return result & 0x7FFFFFFF; } private boolean equalsIgnoreCase(String[] stringArray1, String[] stringArray2) { if(stringArray1.length != stringArray2.length) return false; for(int i = 0; i < stringArray1.length; i++) { if(!stringArray1[i].equalsIgnoreCase(stringArray2[i])) return false; } return true; } private void rehash() { int newCapacity = this.capacity * 2; // Copy tables, note that table entries are already unique, so no need to compare string arrays during probing String[][] newStringArrays = new String[newCapacity][]; for(int i = 0; i < this.stringArrays.length; i++) { String[] current = this.stringArrays[i]; if(current != null) { int index = hashCode(current) % newCapacity; while(newStringArrays[index] != null) { if(++index == newCapacity) index = 0; } newStringArrays[index] = current; } } // Size is unchanged this.stringArrays = newStringArrays; this.capacity = newCapacity; this.threshold *= 2; } } class StringInternUtil { private String[] strings; private int size; private int capacity; private int threshold; public StringInternUtil(int size) { // Protect against small tables if(size < 37) size = 37; this.strings = new String[(int) (size * 1.5f)]; this.capacity = strings.length; this.threshold = size; } public String intern(String string) { // Compute the index into the table int index = hashCode(string) % capacity; // Linear probe in the table, if found return the existing entry String probe; while((probe = strings[index]) != null) { if(string.equalsIgnoreCase(probe)) return probe; if(++index == capacity) index = 0; } strings[index] = string; // Rehash table if threshold exceeded if(++size == threshold) rehash(); // Return the just inserted string return string; } private int hashCode(String string) { int result = 0; char[] charArray = string.toCharArray(); for(int i = 0; i < charArray.length; i++) { char current = charArray[i]; result = 31 * result + Character.toLowerCase(Character.toUpperCase(current)); } return result & 0x7FFFFFFF; } private void rehash() { int newCapacity = this.capacity * 2; // Copy tables, note that table entries are already unique, so no need to compare strings during probing String[] newStrings = new String[newCapacity]; for(int i = 0; i < this.strings.length; i++) { String current = this.strings[i]; if(current != null) { int index = hashCode(current) % newCapacity; while(newStrings[index] != null) { if(++index == newCapacity) index = 0; } newStrings[index] = current; } } // Size is unchanged this.strings = newStrings; this.capacity = newCapacity; this.threshold *= 2; } }