/* * 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; } }