/*
* Copyright 2011 Henry Coles
*
* Licensed 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.pitest.classinfo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.pitest.functional.Option;
public class Repository implements ClassInfoSource {
private final HashFunction hashFunction;
private final Map<ClassName, ClassInfo> knownClasses = new HashMap<ClassName, ClassInfo>();
private final Set<ClassName> unknownClasses = new HashSet<ClassName>();
private final ClassByteArraySource source;
public Repository(final ClassByteArraySource source) {
this(source, new AddlerHash());
}
Repository(final ClassByteArraySource source, final HashFunction hashFunction) {
this.source = source;
this.hashFunction = hashFunction;
}
public boolean hasClass(final ClassName name) {
return this.knownClasses.containsKey(name) || querySource(name).hasSome();
}
public Option<ClassInfo> fetchClass(final Class<?> clazz) { // NO_UCD (test
// only)
return fetchClass(clazz.getName());
}
private Option<ClassInfo> fetchClass(final String name) {
return fetchClass(new ClassName(name));
}
@Override
public Option<ClassInfo> fetchClass(final ClassName name) {
final ClassInfo info = this.knownClasses.get(name);
if (info != null) {
return Option.some(info);
}
final Option<ClassInfo> maybeInfo = nameToClassInfo(name);
if (maybeInfo.hasSome()) {
this.knownClasses.put(name, maybeInfo.value());
}
return maybeInfo;
}
private Option<ClassInfo> nameToClassInfo(final ClassName name) {
final Option<byte[]> bytes = querySource(name);
if (bytes.hasSome()) {
final ClassInfoBuilder classData = ClassInfoVisitor.getClassInfo(name,
bytes.value(), this.hashFunction.hash(bytes.value()));
return contructClassInfo(classData);
} else {
return Option.none();
}
}
public Option<byte[]> querySource(final ClassName name) {
if (this.unknownClasses.contains(name)) {
return Option.none();
}
final Option<byte[]> option = this.source.getBytes(name.asJavaName());
if (option.hasSome()) {
return option;
}
this.unknownClasses.add(name);
return option;
}
private Option<ClassInfo> contructClassInfo(final ClassInfoBuilder classData) {
return Option.some(new ClassInfo(resolveClass(classData.superClass),
resolveClass(classData.outerClass), classData));
}
private ClassPointer resolveClass(final String clazz) {
if (clazz == null) {
return new DefaultClassPointer(null);
} else {
final ClassInfo alreadyResolved = this.knownClasses.get(ClassName
.fromString(clazz));
if (alreadyResolved != null) {
return new DefaultClassPointer(alreadyResolved);
} else {
return new DeferredClassPointer(this, ClassName.fromString(clazz));
}
}
}
}