/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* 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 jetbrains.mps.reloading;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import jetbrains.mps.project.MPSExtentions;
import jetbrains.mps.util.ConditionalIterable;
import jetbrains.mps.util.InternUtil;
import jetbrains.mps.util.NameUtil;
import jetbrains.mps.util.ReadUtil;
import jetbrains.mps.util.containers.EmptyIterable;
import org.jetbrains.mps.util.Condition;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Kostik
*/
class FileClassPathItem extends RealClassPathItem {
private final String myClassPath;
private final Map<String, Set<String>> mySubpackagesCache = new THashMap<String, Set<String>>();
private final Map<String, Set<String>> myAvailableClassesCache = new THashMap<String, Set<String>>();
FileClassPathItem(String classPath) {
myClassPath = classPath;
}
@Deprecated
public String getClassPath() {
return getPath();
}
@Override
public String getPath() {
return myClassPath;
}
@Override
public boolean hasClass(String name) {
String namespace = NameUtil.namespaceFromLongName(name);
String shortname = NameUtil.shortNameFromLongName(name);
if (!myAvailableClassesCache.containsKey(namespace)) {
buildCacheFor(namespace);
}
Set<String> classes = myAvailableClassesCache.get(namespace);
return classes != null && classes.contains(shortname);
}
@Override
public synchronized ClassBytes getClassBytes(String name) {
String namespace = NameUtil.namespaceFromLongName(name);
String shortName = NameUtil.shortNameFromLongName(name);
if (!myAvailableClassesCache.containsKey(namespace)) {
buildCacheFor(namespace);
}
Set<String> classes = myAvailableClassesCache.get(namespace);
if (classes == null || !classes.contains(shortName)) {
return null;
}
String path = myClassPath + File.separatorChar + NameUtil.pathFromNamespace(name) + MPSExtentions.DOT_CLASSFILE;
try {
byte[] bytes = null;
InputStream inp = null;
try {
inp = new FileInputStream(path);
bytes = ReadUtil.read(inp);
} finally {
if (inp != null) {
inp.close();
}
}
return bytes == null ? null : new DefaultClassBytes(bytes, new File(path).toURI().toURL());
} catch (IOException e) {
return null;
}
}
@Override
public URL getResource(String name) {
try {
File resourceFile = new File(myClassPath + File.separator + name.replace('/', File.separatorChar));
if (!resourceFile.exists()) return null;
return resourceFile.toURI().toURL();
} catch (MalformedURLException e) {
return null;
}
}
@Override
public synchronized Iterable<String> getAvailableClasses(String namespace) {
if (!myAvailableClassesCache.containsKey(namespace)) {
buildCacheFor(namespace);
}
Set<String> start = myAvailableClassesCache.get(namespace);
if (start == null) return new EmptyIterable<String>();
Condition<String> cond = className -> !isAnonymous(className);
return new ConditionalIterable<String>(start, cond);
}
@Override
public synchronized Iterable<String> getSubpackages(String namespace) {
if (!mySubpackagesCache.containsKey(namespace)) {
buildCacheFor(namespace);
}
Set<String> result = mySubpackagesCache.get(namespace);
if (result == null) return new EmptyIterable<String>();
return Collections.unmodifiableSet(result);
}
private synchronized void buildCacheFor(String namespace) {
namespace = InternUtil.intern(namespace);
Set<String> subpacks = null;
Set<String> classes = null;
File dir = getModelDir(namespace);
String[] files = dir.list();
if (files != null) {
for (String name : files) {
if (name.endsWith(MPSExtentions.DOT_CLASSFILE)) { //isDirectory is quite expensive operation
if (classes == null) {
classes = new THashSet<String>(files.length);
}
String classname = name.substring(0, name.length() - MPSExtentions.DOT_CLASSFILE.length());
classes.add(InternUtil.intern(classname));
} else {
File file = new File(dir, name);
if (file.isDirectory()) {
if (subpacks == null) {
subpacks = new THashSet<String>();
}
String fqName = namespace.length() > 0 ? namespace + "." + name : name;
subpacks.add(InternUtil.intern(fqName));
}
}
}
}
mySubpackagesCache.put(namespace, subpacks);
myAvailableClassesCache.put(namespace, classes);
}
@Override
public List<RealClassPathItem> flatten() {
List<RealClassPathItem> result = new ArrayList<RealClassPathItem>();
result.add(this);
return result;
}
@Override
public void accept(IClassPathItemVisitor visitor) {
visitor.visit(this);
}
private File getModelDir(String namespace) {
if (namespace == null) namespace = "";
return new File(myClassPath + File.separatorChar + NameUtil.pathFromNamespace(namespace));
}
public String toString() {
return "file-cp: " + myClassPath;
}
}