/*******************************************************************************
* Copyright (c) 2000, 2008 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
* Tim Hanson <thanson@bea.com> - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=137634
*******************************************************************************/
package org.eclipse.jdt.internal.core.builder;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
public class ReferenceCollection {
char[][][] qualifiedNameReferences; // contains no simple names as in just 'a' which is kept in simpleNameReferences instead
char[][] simpleNameReferences;
char[][] rootReferences;
protected ReferenceCollection(char[][][] qualifiedNameReferences, char[][] simpleNameReferences, char[][] rootReferences) {
this.qualifiedNameReferences= internQualifiedNames(qualifiedNameReferences);
this.simpleNameReferences= internSimpleNames(simpleNameReferences, true);
this.rootReferences= internSimpleNames(rootReferences, false);
}
public void addDependencies(String[] typeNameDependencies) {
// if each qualified type name is already known then all of its subNames can be skipped
// and its expected that very few qualified names in typeNameDependencies need to be added
// but could always take 'p1.p2.p3.X' and make all qualified names 'p1' 'p1.p2' 'p1.p2.p3' 'p1.p2.p3.X', then intern
char[][][] qNames= new char[typeNameDependencies.length][][];
for (int i= typeNameDependencies.length; --i >= 0;)
qNames[i]= CharOperation.splitOn('.', typeNameDependencies[i].toCharArray());
qNames= internQualifiedNames(qNames);
next: for (int i= qNames.length; --i >= 0;) {
char[][] qualifiedTypeName= qNames[i];
while (!includes(qualifiedTypeName)) {
if (!includes(qualifiedTypeName[qualifiedTypeName.length - 1])) {
int length= this.simpleNameReferences.length;
System.arraycopy(this.simpleNameReferences, 0, this.simpleNameReferences= new char[length + 1][], 0, length);
this.simpleNameReferences[length]= qualifiedTypeName[qualifiedTypeName.length - 1];
}
if (!insideRoot(qualifiedTypeName[0])) {
int length= this.rootReferences.length;
System.arraycopy(this.rootReferences, 0, this.rootReferences= new char[length + 1][], 0, length);
this.rootReferences[length]= qualifiedTypeName[0];
}
int length= this.qualifiedNameReferences.length;
System.arraycopy(this.qualifiedNameReferences, 0, this.qualifiedNameReferences= new char[length + 1][][], 0, length);
this.qualifiedNameReferences[length]= qualifiedTypeName;
qualifiedTypeName= CharOperation.subarray(qualifiedTypeName, 0, qualifiedTypeName.length - 1);
char[][][] temp= internQualifiedNames(new char[][][] { qualifiedTypeName });
if (temp == EmptyQualifiedNames)
continue next; // qualifiedTypeName is a well known name
qualifiedTypeName= temp[0];
}
}
}
public boolean includes(char[] simpleName) {
for (int i= 0, l= this.simpleNameReferences.length; i < l; i++)
if (simpleName == this.simpleNameReferences[i])
return true;
return false;
}
public boolean includes(char[][] qualifiedName) {
for (int i= 0, l= this.qualifiedNameReferences.length; i < l; i++)
if (qualifiedName == this.qualifiedNameReferences[i])
return true;
return false;
}
/**
* @deprecated
*/
public boolean includes(char[][][] qualifiedNames, char[][] simpleNames) {
return includes(qualifiedNames, simpleNames, null);
}
public boolean includes(char[][][] qualifiedNames, char[][] simpleNames, char[][] rootNames) {
// if either collection of names is null, it means it contained a well known name so we know it already has a match
if (rootNames != null) {
boolean foundRoot= false;
for (int i= 0, l= rootNames.length; !foundRoot && i < l; i++)
foundRoot= insideRoot(rootNames[i]);
if (!foundRoot)
return false;
}
if (simpleNames == null || qualifiedNames == null) {
if (simpleNames == null && qualifiedNames == null) {
if (JavaBuilder.DEBUG)
System.out.println("Found well known match"); //$NON-NLS-1$
return true;
} else if (qualifiedNames == null) {
for (int i= 0, l= simpleNames.length; i < l; i++) {
if (includes(simpleNames[i])) {
if (JavaBuilder.DEBUG)
System.out.println("Found match in well known package to " + new String(simpleNames[i])); //$NON-NLS-1$
return true;
}
}
} else {
for (int i= 0, l= qualifiedNames.length; i < l; i++) {
char[][] qualifiedName= qualifiedNames[i];
if (qualifiedName.length == 1 ? includes(qualifiedName[0]) : includes(qualifiedName)) {
if (JavaBuilder.DEBUG)
System.out.println("Found well known match in " + CharOperation.toString(qualifiedName)); //$NON-NLS-1$
return true;
}
}
}
return false;
}
int sLength= simpleNames.length;
int qLength= qualifiedNames.length;
if (sLength <= qLength) {
for (int i= 0; i < sLength; i++) {
if (includes(simpleNames[i])) {
for (int j= 0; j < qLength; j++) {
char[][] qualifiedName= qualifiedNames[j];
if (qualifiedName.length == 1 ? includes(qualifiedName[0]) : includes(qualifiedName)) {
if (JavaBuilder.DEBUG)
System.out.println("Found match in " + CharOperation.toString(qualifiedName) //$NON-NLS-1$
+ " to " + new String(simpleNames[i])); //$NON-NLS-1$
return true;
}
}
return false;
}
}
} else {
for (int i= 0; i < qLength; i++) {
char[][] qualifiedName= qualifiedNames[i];
if (qualifiedName.length == 1 ? includes(qualifiedName[0]) : includes(qualifiedName)) {
for (int j= 0; j < sLength; j++) {
if (includes(simpleNames[j])) {
if (JavaBuilder.DEBUG)
System.out.println("Found match in " + CharOperation.toString(qualifiedName) //$NON-NLS-1$
+ " to " + new String(simpleNames[j])); //$NON-NLS-1$
return true;
}
}
return false;
}
}
}
return false;
}
public boolean insideRoot(char[] rootName) {
for (int i= 0, l= this.rootReferences.length; i < l; i++)
if (rootName == this.rootReferences[i])
return true;
return false;
}
// When any type is compiled, its methods are verified for certain problems
// the MethodVerifier requests 3 well known types which end up in the reference collection
// having WellKnownQualifiedNames & WellKnownSimpleNames, saves every type 40 bytes
// NOTE: These collections are sorted by length
static final char[][][] WellKnownQualifiedNames= new char[][][] {
TypeConstants.JAVA_LANG_RUNTIMEEXCEPTION,
TypeConstants.JAVA_LANG_THROWABLE,
TypeConstants.JAVA_LANG_OBJECT,
TypeConstants.JAVA_LANG,
new char[][] { TypeConstants.JAVA },
new char[][] { new char[] { 'o', 'r', 'g' } },
new char[][] { new char[] { 'c', 'o', 'm' } },
CharOperation.NO_CHAR_CHAR }; // default package
static final char[][] WellKnownSimpleNames= new char[][] {
TypeConstants.JAVA_LANG_RUNTIMEEXCEPTION[2],
TypeConstants.JAVA_LANG_THROWABLE[2],
TypeConstants.JAVA_LANG_OBJECT[2],
TypeConstants.JAVA,
TypeConstants.LANG,
new char[] { 'o', 'r', 'g' },
new char[] { 'c', 'o', 'm' } };
static final char[][][] EmptyQualifiedNames= new char[0][][];
static final char[][] EmptySimpleNames= CharOperation.NO_CHAR_CHAR;
// each array contains qualified char[][], one for size 2, 3, 4, 5, 6, 7 & the rest
static final int MaxQualifiedNames= 7;
static QualifiedNameSet[] InternedQualifiedNames= new QualifiedNameSet[MaxQualifiedNames];
// each array contains simple char[], one for size 1 to 29 & the rest
static final int MaxSimpleNames= 30;
static NameSet[] InternedSimpleNames= new NameSet[MaxSimpleNames];
static {
for (int i= 0; i < MaxQualifiedNames; i++)
InternedQualifiedNames[i]= new QualifiedNameSet(37);
for (int i= 0; i < MaxSimpleNames; i++)
InternedSimpleNames[i]= new NameSet(37);
}
public static char[][][] internQualifiedNames(StringSet qualifiedStrings) {
if (qualifiedStrings == null)
return EmptyQualifiedNames;
int length= qualifiedStrings.elementSize;
if (length == 0)
return EmptyQualifiedNames;
char[][][] result= new char[length][][];
String[] strings= qualifiedStrings.values;
for (int i= 0, l= strings.length; i < l; i++)
if (strings[i] != null)
result[--length]= CharOperation.splitOn('/', strings[i].toCharArray());
return internQualifiedNames(result);
}
public static char[][][] internQualifiedNames(char[][][] qualifiedNames) {
if (qualifiedNames == null)
return EmptyQualifiedNames;
int length= qualifiedNames.length;
if (length == 0)
return EmptyQualifiedNames;
char[][][] keepers= new char[length][][];
int index= 0;
next: for (int i= 0; i < length; i++) {
char[][] qualifiedName= qualifiedNames[i];
int qLength= qualifiedName.length;
for (int j= 0, m= WellKnownQualifiedNames.length; j < m; j++) {
char[][] wellKnownName= WellKnownQualifiedNames[j];
if (qLength > wellKnownName.length)
break; // all remaining well known names are shorter
if (CharOperation.equals(qualifiedName, wellKnownName))
continue next;
}
// InternedQualifiedNames[0] is for the rest (> 7 & 1)
// InternedQualifiedNames[1] is for size 2...
// InternedQualifiedNames[6] is for size 7
QualifiedNameSet internedNames= InternedQualifiedNames[qLength <= MaxQualifiedNames ? qLength - 1 : 0];
qualifiedName= internSimpleNames(qualifiedName, false);
keepers[index++]= internedNames.add(qualifiedName);
}
if (length > index) {
if (index == 0)
return EmptyQualifiedNames;
System.arraycopy(keepers, 0, keepers= new char[index][][], 0, index);
}
return keepers;
}
/**
* @deprecated
*/
public static char[][] internSimpleNames(StringSet simpleStrings) {
return internSimpleNames(simpleStrings, true);
}
public static char[][] internSimpleNames(StringSet simpleStrings, boolean removeWellKnown) {
if (simpleStrings == null)
return EmptySimpleNames;
int length= simpleStrings.elementSize;
if (length == 0)
return EmptySimpleNames;
char[][] result= new char[length][];
String[] strings= simpleStrings.values;
for (int i= 0, l= strings.length; i < l; i++)
if (strings[i] != null)
result[--length]= strings[i].toCharArray();
return internSimpleNames(result, removeWellKnown);
}
public static char[][] internSimpleNames(char[][] simpleNames, boolean removeWellKnown) {
if (simpleNames == null)
return EmptySimpleNames;
int length= simpleNames.length;
if (length == 0)
return EmptySimpleNames;
char[][] keepers= new char[length][];
int index= 0;
next: for (int i= 0; i < length; i++) {
char[] name= simpleNames[i];
int sLength= name.length;
for (int j= 0, m= WellKnownSimpleNames.length; j < m; j++) {
char[] wellKnownName= WellKnownSimpleNames[j];
if (sLength > wellKnownName.length)
break; // all remaining well known names are shorter
if (CharOperation.equals(name, wellKnownName)) {
if (!removeWellKnown)
keepers[index++]= WellKnownSimpleNames[j];
continue next;
}
}
// InternedSimpleNames[0] is for the rest (> 29)
// InternedSimpleNames[1] is for size 1...
// InternedSimpleNames[29] is for size 29
NameSet internedNames= InternedSimpleNames[sLength < MaxSimpleNames ? sLength : 0];
keepers[index++]= internedNames.add(name);
}
if (length > index) {
if (index == 0)
return EmptySimpleNames;
System.arraycopy(keepers, 0, keepers= new char[index][], 0, index);
}
return keepers;
}
}