/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.file;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.WeakHashMap;
import com.cburch.logisim.tools.Library;
import static com.cburch.logisim.util.LocaleString.*;
class LibraryManager {
public static final LibraryManager instance = new LibraryManager();
private static char desc_sep = '#';
private static abstract class LibraryDescriptor {
abstract boolean concernsFile(File query);
abstract String toDescriptor(Loader loader);
abstract void setBase(Loader loader, LoadedLibrary lib)
throws LoadFailedException;
}
private static class LogisimProjectDescriptor extends LibraryDescriptor {
private File file;
LogisimProjectDescriptor(File file) {
this.file = file;
}
@Override
boolean concernsFile(File query) {
return file.equals(query);
}
@Override
String toDescriptor(Loader loader) {
return "file#" + toRelative(loader, file);
}
@Override
void setBase(Loader loader, LoadedLibrary lib) throws LoadFailedException {
lib.setBase(loader.loadLogisimFile(file));
}
@Override
public boolean equals(Object other) {
if (!(other instanceof LogisimProjectDescriptor)) {
return false;
}
LogisimProjectDescriptor o = (LogisimProjectDescriptor) other;
return this.file.equals(o.file);
}
@Override
public int hashCode() {
return file.hashCode();
}
}
private static class JarDescriptor extends LibraryDescriptor {
private File file;
private String className;
JarDescriptor(File file, String className) {
this.file = file;
this.className = className;
}
@Override
boolean concernsFile(File query) {
return file.equals(query);
}
@Override
String toDescriptor(Loader loader) {
return "jar#" + toRelative(loader, file) + desc_sep + className;
}
@Override
void setBase(Loader loader, LoadedLibrary lib) throws LoadFailedException {
lib.setBase(loader.loadJarFile(file, className));
}
@Override
public boolean equals(Object other) {
if (!(other instanceof JarDescriptor)) {
return false;
}
JarDescriptor o = (JarDescriptor) other;
return this.file.equals(o.file) && this.className.equals(o.className);
}
@Override
public int hashCode() {
return file.hashCode() * 31 + className.hashCode();
}
}
private HashMap<LibraryDescriptor,WeakReference<LoadedLibrary>> fileMap;
private WeakHashMap<LoadedLibrary,LibraryDescriptor> invMap;
private LibraryManager() {
fileMap = new HashMap<LibraryDescriptor,WeakReference<LoadedLibrary>>();
invMap = new WeakHashMap<LoadedLibrary,LibraryDescriptor>();
ProjectsDirty.initialize();
}
void setDirty(File file, boolean dirty) {
LoadedLibrary lib = findKnown(file);
if (lib != null) {
lib.setDirty(dirty);
}
}
Collection<LogisimFile> getLogisimLibraries() {
ArrayList<LogisimFile> ret = new ArrayList<LogisimFile>();
for (LoadedLibrary lib : invMap.keySet()) {
if (lib.getBase() instanceof LogisimFile) {
ret.add((LogisimFile) lib.getBase());
}
}
return ret;
}
public Library loadLibrary(Loader loader, String desc) {
// It may already be loaded.
// Otherwise we'll have to decode it.
int sep = desc.indexOf(desc_sep);
if (sep < 0) {
loader.showError(getFromLocale("fileDescriptorError", desc));
return null;
}
String type = desc.substring(0, sep);
String name = desc.substring(sep + 1);
if (type.equals("")) {
Library ret = loader.getBuiltin().getLibrary(name);
if (ret == null) {
loader.showError(getFromLocale("fileBuiltinMissingError", name));
return null;
}
return ret;
} else if (type.equals("file")) {
File toRead = loader.getFileFor(name, Loader.LOGISIM_FILTER);
return loadLogisimLibrary(loader, toRead);
} else if (type.equals("jar")) {
int sepLoc = name.lastIndexOf(desc_sep);
String fileName = name.substring(0, sepLoc);
String className = name.substring(sepLoc + 1);
File toRead = loader.getFileFor(fileName, Loader.JAR_FILTER);
return loadJarLibrary(loader, toRead, className);
} else {
loader.showError(getFromLocale("fileTypeError",
type, desc));
return null;
}
}
public LoadedLibrary loadLogisimLibrary(Loader loader, File toRead) {
LoadedLibrary ret = findKnown(toRead);
if (ret != null) {
return ret;
}
try {
ret = new LoadedLibrary(loader.loadLogisimFile(toRead));
} catch (LoadFailedException e) {
loader.showError(e.getMessage());
return null;
}
LogisimProjectDescriptor desc = new LogisimProjectDescriptor(toRead);
fileMap.put(desc, new WeakReference<LoadedLibrary>(ret));
invMap.put(ret, desc);
return ret;
}
public LoadedLibrary loadJarLibrary(Loader loader, File toRead, String className) {
JarDescriptor jarDescriptor = new JarDescriptor(toRead, className);
LoadedLibrary ret = findKnown(jarDescriptor);
if (ret != null) {
return ret;
}
try {
ret = new LoadedLibrary(loader.loadJarFile(toRead, className));
} catch (LoadFailedException e) {
loader.showError(e.getMessage());
return null;
}
fileMap.put(jarDescriptor, new WeakReference<LoadedLibrary>(ret));
invMap.put(ret, jarDescriptor);
return ret;
}
public void reload(Loader loader, LoadedLibrary lib) {
LibraryDescriptor descriptor = invMap.get(lib);
if (descriptor == null) {
loader.showError(getFromLocale("unknownLibraryFileError",
lib.getDisplayName()));
} else {
try {
descriptor.setBase(loader, lib);
} catch (LoadFailedException e) {
loader.showError(e.getMessage());
}
}
}
public Library findReference(LogisimFile file, File query) {
for (Library lib : file.getLibraries()) {
LibraryDescriptor desc = invMap.get(lib);
if (desc != null && desc.concernsFile(query)) {
return lib;
}
if (lib instanceof LoadedLibrary) {
LoadedLibrary loadedLib = (LoadedLibrary) lib;
if (loadedLib.getBase() instanceof LogisimFile) {
LogisimFile loadedProj = (LogisimFile) loadedLib.getBase();
Library ret = findReference(loadedProj, query);
if (ret != null) {
return lib;
}
}
}
}
return null;
}
public void fileSaved(Loader loader, File dest, File oldFile, LogisimFile file) {
LoadedLibrary old = findKnown(oldFile);
if (old != null) {
old.setDirty(false);
}
LoadedLibrary lib = findKnown(dest);
if (lib != null) {
LogisimFile clone = file.cloneLogisimFile(loader);
clone.setName(file.getName());
clone.setDirty(false);
lib.setBase(clone);
}
}
public String getDescriptor(Loader loader, Library lib) {
if (loader.getBuiltin().getLibraries().contains(lib)) {
return desc_sep + lib.getName();
} else {
LibraryDescriptor desc = invMap.get(lib);
if (desc != null) {
return desc.toDescriptor(loader);
} else {
throw new LoaderException(getFromLocale("fileDescriptorUnknownError", lib.getDisplayName()));
}
}
}
private LoadedLibrary findKnown(Object key) {
WeakReference<LoadedLibrary> retLibRef;
retLibRef = fileMap.get(key);
if (retLibRef == null) {
return null;
} else {
LoadedLibrary retLib = retLibRef.get();
if (retLib == null) {
fileMap.remove(key);
return null;
} else {
return retLib;
}
}
}
private static String toRelative(Loader loader, File file) {
File currentDirectory = loader.getCurrentDirectory();
if (currentDirectory == null) {
try {
return file.getCanonicalPath();
} catch (IOException e) {
return file.toString();
}
}
File fileDir = file.getParentFile();
if (fileDir != null) {
if (currentDirectory.equals(fileDir)) {
return file.getName();
} else if (currentDirectory.equals(fileDir.getParentFile())) {
return fileDir.getName() + "/" + file.getName();
} else if (fileDir.equals(currentDirectory.getParentFile())) {
return "../" + file.getName();
}
}
try {
return file.getCanonicalPath();
} catch (IOException e) {
return file.toString();
}
}
}