/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Library.java
*
* Copyright (c) 2003 Sun Microsystems and Static Free Software
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.database.hierarchy;
import com.sun.electric.database.EObjectInputStream;
import com.sun.electric.database.EObjectOutputStream;
import com.sun.electric.database.IdMapper;
import com.sun.electric.database.ImmutableLibrary;
import com.sun.electric.database.LibraryBackup;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.constraint.Constraints;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.id.LibId;
import com.sun.electric.database.text.CellName;
import com.sun.electric.database.text.Pref;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.user.ErrorLogger;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.prefs.Preferences;
/**
* A Library represents a collection of Cells.
* To find of a Library, you use Electric.getLibrary(String name).
* Use Electric.newLibrary(String name) to create a new library, or
* Electric.getCurrent() to get the current Library.
* <p>
* Once you have a Library, you can create a new Cell in it, find an existing
* Cell, get an Enumeration of all Cells, or find the Cell that the user
* is currently editing.
*/
public class Library extends ElectricObject implements Comparable<Library> {
/** key of Variable holding font associations. */
public static final Variable.Key FONT_ASSOCIATIONS = Variable.newKey("LIB_font_associations");
// ------------------------ private data ------------------------------
// /** library has changed significantly */ private static final int LIBCHANGEDMAJOR = 01;
// /** set to see library in explorer */ private static final int OPENINEXPLORER = 02;
/** set if library came from disk */
private static final int READFROMDISK = 04;
// /** internal units in library (see INTERNALUNITS) */ private static final int LIBUNITS = 070;
// /** right shift for LIBUNITS */ private static final int LIBUNITSSH = 3;
// /** library has changed insignificantly */ private static final int LIBCHANGEDMINOR = 0100;
/** library is "hidden" (clipboard library) */
public static final int HIDDENLIBRARY = 0200;
// /** library is unwanted (used during input) */ private static final int UNWANTEDLIB = 0400;
/** Database to which this Library belongs. */
private final EDatabase database;
/** persistent data of this Library. */
private ImmutableLibrary d;
/** list of referenced libs */
private final List<Library> referencedLibs = new ArrayList<Library>();
/** Last backup of this Library */
LibraryBackup backup;
/** list of Cells in this library */
final TreeMap<CellName, Cell> cells = new TreeMap<CellName, Cell>();
// ----------------- private and protected methods --------------------
/**
* The constructor is never called. Use the factor method "newInstance" instead.
*/
Library(EDatabase database, ImmutableLibrary d) {
if (database == null) {
throw new NullPointerException();
}
this.database = database;
this.d = d;
backup = new LibraryBackup(d, true, LibId.NULL_ARRAY);
}
/**
* This method is a factory to create new libraries.
* A Library has both a name and a file.
* @param libName the name of the library (for example, "gates").
* Library names must be unique, and they must not contain spaces or colons.
* @param libFile the URL to the disk file (for example "/home/strubin/gates.elib").
* If the Library is being created, the libFile can be null.
* If the Library file is given and it points to an existing file, then the I/O system
* can be told to read that file and populate the Library.
* @return the Library object.
*/
public static Library newInstance(String libName, URL libFile) {
// make sure the name is legal
String legalName = LibId.legalLibraryName(libName);
if (legalName == null) {
return null;
}
if (legalName != libName) {
System.out.println("Warning: library '" + libName + "' renamed to '" + legalName + "'");
}
// see if the library name already exists
EDatabase database = EDatabase.currentDatabase();
Library existingLibrary = database.findLibrary(legalName);
if (existingLibrary != null) {
System.out.println("Error: library '" + legalName + "' already exists");
return existingLibrary;
}
// create the library
return newInstance(database, database.getIdManager().newLibId(legalName), libFile);
}
/**
* This method is a factory to create new libraries.
* A Library has both a name and a file.
* @param libId ID of new Library.
* @param libFile the URL to the disk file (for example "/home/strubin/gates.elib").
* If the Library is being created, the libFile can be null.
* If the Library file is given and it points to an existing file, then the I/O system
* can be told to read that file and populate the Library.
* @return the Library object.
* @throws NullPointerException if libId or legalName is null.
*/
private static Library newInstance(EDatabase edb, LibId libId, URL libFile) {
// create the library
ImmutableLibrary d = ImmutableLibrary.newInstance(libId, libFile, null);
Library lib = new Library(edb, d);
// add the library to the global list
edb.addLib(lib);
// always broadcast library changes
// Undo.setNextChangeQuiet(false);
edb.unfreshSnapshot();
Constraints.getCurrent().newObject(lib);
return lib;
}
/**
* Method for serialization.
*/
private Object writeReplace() {
return new LibraryKey(this);
}
private static class LibraryKey extends EObjectInputStream.Key<Library> {
public LibraryKey() {
}
private LibraryKey(Library lib) {
super(lib);
}
@Override
public void writeExternal(EObjectOutputStream out, Library lib) throws IOException {
if (lib.getDatabase() != out.getDatabase() || !lib.isLinked()) {
throw new NotSerializableException(lib + " not linked");
}
out.writeObject(lib.getId());
}
@Override
public Library readExternal(EObjectInputStream in) throws IOException, ClassNotFoundException {
LibId libId = (LibId) in.readObject();
Library lib = libId.inDatabase(in.getDatabase());
if (lib == null) {
throw new InvalidObjectException(libId + " not linked");
}
return lib;
}
}
/**
* Method to delete this Library.
* @param reason the reason for deleting this library (replacement or deletion).
* @return true if the library was deleted.
* Returns false on error.
*/
public boolean kill(String reason) {
if (!isLinked()) {
System.out.println("Library already killed");
return false;
}
// cannot delete the current library
Library newCurLib = null;
if (getCurrent() == this) {
// find another library
for (Library lib : database.libraries.values()) {
if (lib == this) {
continue;
}
if (lib.isHidden()) {
continue;
}
newCurLib = lib;
break;
}
// if (newCurLib == null)
// {
// System.out.println("Cannot delete the last library");
// Job.getUserInterface().showInformationMessage("Cannot delete the last "+toString(),
// "Close library");
// return false;
// }
}
// make sure it is in the list of libraries
if (database.libraries.get(getName()) != this) {
System.out.println("Cannot delete library " + this);
Job.getUserInterface().showErrorMessage("Cannot delete " + toString(),
"Close library");
return false;
}
// make sure none of these cells are referenced by other libraries
boolean referenced = false;
for (Library lib : database.libraries.values()) {
if (lib == this) {
continue;
}
for (Iterator<Cell> cIt = lib.getCells(); cIt.hasNext();) {
Cell cell = cIt.next();
for (Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext();) {
NodeInst ni = nIt.next();
if (ni.isCellInstance()) {
Cell subCell = (Cell) ni.getProto();
if (subCell.getLibrary() == this) {
Job.getUserInterface().showErrorMessage("Library close failed. Cannot " + reason + " " + toString()
+ " because one of its cells (" + subCell.noLibDescribe() + ") is being used (by " + cell.libDescribe() + ")",
"Close library");
referenced = true;
break;
}
}
}
if (referenced) {
break;
}
}
if (referenced) {
break;
}
}
if (referenced) {
return false;
}
// remove all cells in the library
erase();
for (Library lib : database.libraries.values()) {
if (lib == this) {
continue;
}
lib.removeReferencedLib(this);
}
// remove it from the list of libraries
database.removeLib(getId());
// set the new current library if appropriate
if (newCurLib != null) {
Job.setCurrentLibraryInJob(newCurLib);
}
// always broadcast library changes
// Undo.setNextChangeQuiet(false);
database.unfreshSnapshot();
Constraints.getCurrent().killObject(this);
return true;
}
/**
* Method to remove all contents from this Library.
*/
public void erase() {
// remove all cells in the library
for (Iterator<Cell> it = getCells(); it.hasNext();) {
Cell c = it.next();
c.kill();
}
cells.clear();
}
/**
* Method to add a Cell to this Library.
* @param c the Cell to add.
*/
void addCell(Cell c) {
CellName cn = c.getCellName();
// sanity check: make sure Cell isn't already in the list
synchronized (cells) {
if (cells.containsKey(cn)) {
System.out.println("Tried to re-add a cell to a library: " + c);
return;
}
cells.put(cn, c);
updateNewestVersions();
}
setChanged();
}
/**
* Method to remove a Cell from this Library.
* @param c the Cell to remove.
*/
void removeCell(Cell c) {
CellName cn = c.getCellName();
// sanity check: make sure Cell is in the list
synchronized (cells) {
if (cells.get(cn) != c) {
System.out.println("Tried to remove a non-existant Cell from a library: " + c);
return;
}
cells.remove(cn);
c.newestVersion = null;
updateNewestVersions();
}
setChanged();
}
/**
* Collect cells from database snapshot into cells list of this Library.
*/
void collectCells() {
synchronized (cells) {
cells.clear();
for (int cellIndex = 0; cellIndex < database.linkedCells.size(); cellIndex++) {
Cell cell = database.getCell(cellIndex);
if (cell == null || cell.getLibrary() != this) {
continue;
}
cells.put(cell.getCellName(), cell);
}
updateNewestVersions();
}
}
/**
* Update newestVersion fields of cells in this Library.
*/
private void updateNewestVersions() {
Cell newestVersion = null;
for (Cell cell : cells.values()) {
if (newestVersion == null || !newestVersion.getName().equals(cell.getName()) || newestVersion.getView() != cell.getView()) {
newestVersion = cell;
}
cell.newestVersion = newestVersion;
}
}
/**
* Adds lib as a referenced library. This also checks the dependency
* would create a circular dependency, in which case the reference is
* not logged, and the method returns a LibraryDependency object.
* If everything is ok, the method returns null.
* @param lib the library to be added as a referenced lib
* @return null if ok, a LibraryDependency object if this would create circular dependency
*/
LibraryDependency addReferencedLib(Library lib) {
synchronized (referencedLibs) {
if (referencedLibs.contains(lib)) {
return null; // already a referenced lib, is ok
}
}
// check recursively if there is a circular dependency
List<Library> libDependencies = new ArrayList<Library>();
if (lib.isReferencedLib(this, libDependencies)) {
// there is a dependency
// trace the dependency (this is an expensive operation)
LibraryDependency d = new LibraryDependency();
d.startLib = lib;
d.finalRefLib = this;
Library startLib = lib;
for (Library refLib : libDependencies) {
// startLib references refLib. Find out why
boolean found = false;
// find a cell instance that creates dependency
for (Iterator<Cell> itCell = startLib.getCells(); itCell.hasNext();) {
Cell c = itCell.next();
for (Iterator<NodeInst> it = c.getNodes(); it.hasNext();) {
NodeInst ni = it.next();
if (ni.isCellInstance()) {
Cell cc = (Cell) ni.getProto();
if (cc.getLibrary() == refLib) {
d.dependencies.add(c);
d.dependencies.add(cc);
found = true;
break;
}
}
}
if (found) {
break;
}
}
if (!found) {
System.out.println("ERROR: Library.addReferencedLib dependency trace failed inexplicably");
}
startLib = refLib;
}
return d;
}
// would not create circular dependency, add and return
synchronized (referencedLibs) {
referencedLibs.add(lib);
updateBackup(d, backup.modified, backupReferencedLibs(backup.referencedLibs));
}
return null;
}
/**
* Try to remove lib as a referenced library. If it is no longer referenced,
* it is removed as a reference library.
* @param lib the reference library that may no longer be referenced
*/
void removeReferencedLib(Library lib) {
if (lib == this) {
return; // we don't store references to self
}
synchronized (referencedLibs) {
if (!referencedLibs.contains(lib)) {
return;
}
// assert(referencedLibs.contains(lib));
}
boolean refFound = false;
for (Iterator<Cell> itCell = getCells(); itCell.hasNext();) {
Cell c = itCell.next();
for (Iterator<NodeInst> it = c.getNodes(); it.hasNext();) {
NodeInst ni = it.next();
if (ni.isCellInstance()) {
Cell cc = (Cell) ni.getProto();
if (cc.getLibrary() == lib) {
refFound = true;
break;
}
}
}
if (refFound) {
break;
}
}
if (!refFound) {
// no instance that would create reference found, ok to remove reference
synchronized (referencedLibs) {
referencedLibs.remove(lib);
updateBackup(d, backup.modified, backupReferencedLibs(backup.referencedLibs));
}
}
}
/**
* Returns true if this Library directly references the specified Library 'lib'.
* It does not return true if the library is referenced through another library.
* @param lib the possible referenced library
* @return true if the library is directly referenced by this library, false otherwise
*/
public boolean referencesLib(Library lib) {
synchronized (referencedLibs) {
if (referencedLibs.contains(lib)) {
return true;
}
}
return false;
}
/**
* Checks to see if lib is referenced by this library, or by
* any libraries this library references, recursively.
* @param lib the lib to check if it is a referenced lib
* @return true if it is through any number of references, false otherwise
*/
private boolean isReferencedLib(Library lib, List<Library> libDepedencies) {
List<Library> reflibsCopy = new ArrayList<Library>();
synchronized (referencedLibs) {
if (referencedLibs.contains(lib)) {
libDepedencies.add(lib);
return true;
}
reflibsCopy.addAll(referencedLibs);
}
for (Library reflib : reflibsCopy) {
// if reflib already in dependency list, ignore
if (libDepedencies.contains(reflib)) {
continue;
}
// check recursively
libDepedencies.add(reflib);
if (reflib.isReferencedLib(lib, libDepedencies)) {
return true;
}
// remove reflib in accumulated list, try again
libDepedencies.remove(reflib);
}
return false;
}
static class LibraryDependency {
private List<Cell> dependencies;
private Library startLib;
private Library finalRefLib;
private LibraryDependency() {
dependencies = new ArrayList<Cell>();
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(startLib + " depends on " + finalRefLib + " through the following references:\n");
for (Iterator<Cell> it = dependencies.iterator(); it.hasNext();) {
Cell libCell = it.next();
Cell instance = it.next();
buf.append(" " + libCell.libDescribe() + " instantiates " + instance.libDescribe() + "\n");
}
return buf.toString();
}
}
// ----------------- public interface --------------------
/**
* Returns persistent data of this Library.
* @return persistent data of this Library.
*/
@Override
public ImmutableLibrary getD() {
return d;
}
/**
* Modifies persistend data of this Library.
* @param newD new persistent data.
* @return true if persistent data was modified.
*/
private boolean setD(ImmutableLibrary newD) {
checkChanging();
ImmutableLibrary oldD = d;
if (newD == oldD) {
return false;
}
d = newD;
// setChanged();
assert isLinked();
updateBackup(d, true, backup.referencedLibs);
Constraints.getCurrent().modifyLibrary(this, oldD);
return true;
}
/**
* Method to add a Variable on this Library.
* It may add repaired copy of this Variable in some cases.
* @param var Variable to add.
*/
public void addVar(Variable var) {
setD(getD().withVariable(var));
}
/**
* Method to delete a Variable from this Library.
* @param key the key of the Variable to delete.
*/
public void delVar(Variable.Key key) {
setD(getD().withoutVariable(key));
}
/**
* Method to return LibId of this Library.
* LibId identifies Library independently of threads.
* @return LibId of this Library.
*/
public LibId getId() {
return d.libId;
}
/**
* Returns a Library by LibId.
* Returns null if the Library is not linked to the database.
* @param libId LibId to find.
* @return Library or null.
*/
public static Library inCurrentThread(LibId libId) {
return EDatabase.currentDatabase().getLib(libId);
}
/**
* Returns true if this Library is linked into database.
* @return true if this Library is linked into database.
*/
public boolean isLinked() {
return inCurrentThread(getId()) == this;
}
/**
* Returns database to which this Library belongs.
* @return database to which this Library belongs.
*/
public EDatabase getDatabase() {
return database;
}
/*
* Low-level method to backup this Library to LibraryBackup.
* @return LibraryBackup which is the backup of this Library.
*/
public LibraryBackup backup() {
return backup;
}
void recover(LibraryBackup recoverBackup) {
checkUndoing();
backup = recoverBackup;
this.d = recoverBackup.d;
referencedLibs.clear();
for (LibId libId : recoverBackup.referencedLibs) {
referencedLibs.add(database.getLib(libId));
}
}
void checkFresh(LibraryBackup libBackup) {
assert d == libBackup.d;
assert backup == libBackup;
assert libBackup.referencedLibs.length == referencedLibs.size();
for (int i = 0; i < libBackup.referencedLibs.length; i++) {
assert libBackup.referencedLibs[i] == referencedLibs.get(i).getId();
}
}
private LibId[] backupReferencedLibs(LibId[] oldReferencedLibs) {
int numRefs = Math.min(oldReferencedLibs.length, referencedLibs.size());
int matchedRefs = 0;
while (matchedRefs < numRefs && oldReferencedLibs[matchedRefs] == referencedLibs.get(matchedRefs).getId()) {
matchedRefs++;
}
if (matchedRefs == oldReferencedLibs.length && matchedRefs == referencedLibs.size()) {
return oldReferencedLibs;
}
LibId[] newRefs = new LibId[referencedLibs.size()];
System.arraycopy(oldReferencedLibs, 0, newRefs, 0, matchedRefs);
for (int i = matchedRefs; i < referencedLibs.size(); i++) {
newRefs[i] = referencedLibs.get(i).getId();
}
return newRefs;
}
public static void repairAllLibraries() {
ErrorLogger errorLogger = ErrorLogger.newInstance("Repair Libraries");
for (Iterator<Library> it = Library.getLibraries(); it.hasNext();) {
Library l = it.next();
l.checkAndRepair(true, errorLogger);
}
System.out.println("Repair Libraries: " + errorLogger.getNumErrors() + " errors, "
+ errorLogger.getNumWarnings() + " warnings");
}
/**
* Method to check and repair data structure errors in this Library.
*/
public int checkAndRepair(boolean repair, ErrorLogger errorLogger) {
int errorCount = 0;
boolean verbose = !isHidden();
if (verbose) {
System.out.print("Checking " + this);
if (repair) {
System.out.print(" for repair");
}
}
for (Iterator<Cell> it = getCells(); it.hasNext();) {
Cell cell = it.next();
errorCount += cell.checkAndRepair(repair, errorLogger);
}
if (errorCount != 0) {
if (repair) {
if (verbose) {
System.out.println("... library repaired");
}
// setChanged();
} else {
if (verbose) {
System.out.println("... library has to be repaired");
}
}
} else {
if (verbose) {
System.out.println("... library checked");
}
}
return errorCount;
}
/**
* Method to check invariants in this Library.
* @exception AssertionError if invariants are not valid
*/
protected void check() {
assert getD() == backup.d;
assert backup.referencedLibs.length == referencedLibs.size();
super.check();
String libName = d.libId.libName;
assert libName != null;
assert libName.length() > 0;
assert libName.indexOf(' ') == -1 && libName.indexOf(':') == -1 : libName;
for (int i = 0; i < referencedLibs.size(); i++) {
Library rLib = referencedLibs.get(i);
assert rLib.isLinked() && rLib.database == database;
assert backup.referencedLibs[i] == referencedLibs.get(i).getId();
}
HashSet<Cell.CellGroup> cellGroups = new HashSet<Cell.CellGroup>();
String protoName = null;
Cell.CellGroup cellGroup = null;
Cell newestVersion = null;
for (Map.Entry<CellName, Cell> e : cells.entrySet()) {
CellName cn = e.getKey();
Cell cell = e.getValue();
assert cell.isLinked() && cell.getDatabase() == database;
assert cell.getCellName() == cn;
assert cell.getLibrary() == this;
if (protoName == null || !cell.getName().equals(protoName)) {
protoName = cell.getName();
cellGroup = cell.getCellGroup();
assert cellGroup != null : cell;
cellGroups.add(cellGroup);
newestVersion = cell;
}
if (cell.getView() != newestVersion.getView()) {
newestVersion = cell;
}
assert cell.getCellGroup() == cellGroup : cell;
assert cell.newestVersion == newestVersion;
}
for (Iterator<Cell.CellGroup> it = cellGroups.iterator(); it.hasNext();) {
cellGroup = it.next();
cellGroup.check();
}
}
private void setFlag(int mask, boolean value) {
setD(d.withFlags(value ? d.flags | mask : d.flags & ~mask));
}
private boolean isFlag(int mask) {
return (d.flags & mask) != 0;
}
/**
* Method to indicate that this Library has changed.
*/
public void setChanged() {
checkChanging();
if (!isChanged()) {
updateBackup(d, true, backup.referencedLibs);
}
}
/**
* Method to indicate that this Library has not changed.
*/
public void clearChanged() {
checkChanging();
for (Cell cell : cells.values()) {
cell.clearModified();
}
if (isChanged()) {
updateBackup(d, false, backup.referencedLibs);
}
}
private void updateBackup(ImmutableLibrary d, boolean modified, LibId[] referencedLibs) {
backup = new LibraryBackup(d, modified, referencedLibs);
database.unfreshSnapshot();
}
/**
* Method to return true if this Library has changed.
* @return true if this Library has changed.
*/
public boolean isChanged() {
return backup.modified;
}
/**
* Method to indicate that this Library came from disk.
* Libraries that come from disk are saved without a file-selection dialog.
*/
public void setFromDisk() {
setFlag(READFROMDISK, true);
}
/**
* Method to indicate that this Library did not come from disk.
* Libraries that come from disk are saved without a file-selection dialog.
*/
public void clearFromDisk() {
setFlag(READFROMDISK, false);
}
/**
* Method to return true if this Library came from disk.
* Libraries that come from disk are saved without a file-selection dialog.
* @return true if this Library came from disk.
*/
public boolean isFromDisk() {
return isFlag(READFROMDISK);
}
/**
* Method to indicate that this Library is hidden.
* Hidden libraries are not seen by the user.
* For example, the "clipboard" library is hidden because it is only used
* internally for copying and pasting circuitry.
*/
public void setHidden() {
setFlag(HIDDENLIBRARY, true);
}
/**
* Method to indicate that this Library is not hidden.
* Hidden libraries are not seen by the user.
* For example, the "clipboard" library is hidden because it is only used
* internally for copying and pasting circuitry.
*/
public void clearHidden() {
setFlag(HIDDENLIBRARY, false);
}
/**
* Method to return true if this Library is hidden.
* Hidden libraries are not seen by the user.
* For example, the "clipboard" library is hidden because it is only used
* internally for copying and pasting circuitry.
* @return true if this Library is hidden.
*/
public boolean isHidden() {
return isFlag(HIDDENLIBRARY);
}
/**
* Method to return the current Library.
* @return the current Library.
*/
public static Library getCurrent() {
return Job.getUserInterface().getCurrentLibrary();
}
/**
* Low-level method to get the user bits.
* The "user bits" are a collection of flags that are more sensibly accessed
* through special methods.
* This general access to the bits is required because the ELIB
* file format stores it as a full integer.
* This should not normally be called by any other part of the system.
* @return the "user bits".
*/
public int lowLevelGetUserBits() {
return d.flags;
}
/**
* Low-level method to set the user bits.
* The "user bits" are a collection of flags that are more sensibly accessed
* through special methods.
* This general access to the bits is required because the ELIB
* file format stores it as a full integer.
* This should not normally be called by any other part of the system.
* @param userBits the new "user bits".
*/
public void lowLevelSetUserBits(int userBits) {
setD(d.withFlags(userBits));
}
/**
* Get list of cells contained in other libraries
* that refer to cells contained in this library
* @param elib to search for
* @return list of cells refering to elements in this library
*/
public static Set<Cell> findReferenceInCell(Library elib) {
return EDatabase.currentDatabase().findReferenceInCell(elib);
}
/**
* Method to find a Library with the specified name.
* @param libName the name of the Library.
* Note that this is the Library name, and not the Library file.
* @return the Library, or null if there is no known Library by that name.
*/
public static Library findLibrary(String libName) {
return EDatabase.currentDatabase().findLibrary(libName);
}
/**
* Method to return an iterator over all libraries.
* @return an iterator over all libraries.
*/
public static Iterator<Library> getLibraries() {
return EDatabase.currentDatabase().getLibraries();
}
/**
* Method to return the number of libraries.
* @return the number of libraries.
*/
public static int getNumLibraries() {
return EDatabase.currentDatabase().getNumLibraries();
}
/**
* Method to return an iterator over all visible libraries.
* @return an iterator over all visible libraries.
*/
public static List<Library> getVisibleLibraries() {
return EDatabase.currentDatabase().getVisibleLibraries();
}
/**
* Method to return the name of this Library.
* @return the name of this Library.
*/
public String getName() {
return d.libId.libName;
}
/**
* Method to set the name of this Library.
* @param libName the new name of this Library.
* @return mapping of Library/Cell/Export ids, null if the library was renamed.
*/
public IdMapper setName(String libName) {
if (d.libId.libName.equals(libName)) {
return null;
}
// make sure the name is legal
if (LibId.legalLibraryName(libName) != libName) {
System.out.println("Error: '" + libName + "' is not a valid name");
return null;
}
Library already = findLibrary(libName);
if (already != null) {
System.out.println("Already a library called " + already.getName());
return null;
}
Snapshot oldSnapshot = database.backup();
LibId newLibId = oldSnapshot.idManager.newLibId(libName);
IdMapper idMapper = IdMapper.renameLibrary(oldSnapshot, d.libId, newLibId);
Snapshot newSnapshot = oldSnapshot.withRenamedIds(idMapper, null, null);
LibraryBackup[] libBackups = newSnapshot.libBackups.toArray(new LibraryBackup[newSnapshot.libBackups.size()]);
LibraryBackup libBackup = libBackups[newLibId.libIndex];
String newLibFile = TextUtils.getFilePath(d.libFile) + libName;
String extension = TextUtils.getExtension(d.libFile);
if (extension.length() > 0) {
newLibFile += "." + extension;
}
URL libFile = TextUtils.makeURLToFile(newLibFile);
libBackups[newLibId.libIndex] = new LibraryBackup(libBackup.d.withLibFile(libFile), true, libBackup.referencedLibs);
newSnapshot = newSnapshot.with(null, null, null, libBackups);
checkChanging();
boolean isCurrent = getCurrent() == this;
database.lowLevelSetCanUndoing(true);
database.undo(newSnapshot);
database.lowLevelSetCanUndoing(false);
Constraints.getCurrent().renameIds(idMapper);
Library newLib = database.getLib(newLibId);
if (isCurrent) {
Job.setCurrentLibraryInJob(newLib);
}
return idMapper;
// String oldName = d.libId.libName;
// lowLevelRename(libName);
// Constraints.getCurrent().renameObject(this, oldName);
//// setChanged();
// assert isLinked();
// database.unfreshSnapshot();
// for (Cell cell: cells.values())
// cell.notifyRename();
// return this;
}
// /**
// * Method to rename this Library.
// * This method is for low-level use by the database, and should not be called elsewhere.
// * @param libName the new name of the Library.
// */
// private void lowLevelRename(String libName)
// {
// String newLibFile = TextUtils.getFilePath(d.libFile) + libName;
// String extension = TextUtils.getExtension(d.libFile);
// if (extension.length() > 0) newLibFile += "." + extension;
// URL libFile = TextUtils.makeURLToFile(newLibFile);
//
// database.libraries.remove(d.libName);
// setD(d.withName(libName, libFile));
// database.libraries.put(libName, this);
// assert isLinked();
// database.unfreshSnapshot();
//
// Cell curCell = getCurCell();
// prefs = allPrefs.node(libName);
// prefs.put("LIB", libName);
// curCellPref = null;
// setCurCell(curCell);
// for (Iterator<Cell> it = getCells(); it.hasNext(); ) {
// Cell cell = it.next();
// cell.expandStatusChanged();
// }
// }
/**
* Method to return the URL of this Library.
* @return the URL of this Library.
*/
public URL getLibFile() {
return d.libFile;
}
/**
* Method to set the URL of this Library.
* @param libFile the new URL of this Library.
*/
public void setLibFile(URL libFile) {
setD(d.withLibFile(libFile));
}
/**
* Compares two <code>Library</code> objects.
* @param that the Library to be compared.
* @return the result of comparison.
*/
public int compareTo(Library that) {
return TextUtils.STRING_NUMBER_ORDER.compare(getName(), that.getName());
}
/**
* Returns a printable version of this Library.
* @return a printable version of this Library.
*/
public String toString() {
return "library '" + getName() + "'";
}
// ----------------- cells --------------------
/**
* Method to get the current Cell in this Library.
* @return the current Cell in this Library.
* Returns NULL if there is no current Cell.
*/
public Cell getCurCell() {
Preferences libPrefs = Pref.getLibraryPreferences(getId());
String key = "CurrentCell";
String cellName = libPrefs.get(key, "");
if (cellName.length() == 0) {
return null;
}
Cell cell = this.findNodeProto(cellName);
if (cell == null) {
libPrefs.remove(key);
}
return cell;
}
/**
* Method to set the current Cell in this Library.
* @param curCell the new current Cell in this Library.
*/
public void setCurCell(Cell curCell) {
Preferences libPrefs = Pref.getLibraryPreferences(getId());
String key = "CurrentCell";
if (curCell == null) {
libPrefs.remove(key);
} else {
libPrefs.put(key, curCell.noLibDescribe());
}
}
public static Cell findCellInLibraries(String cellName, View view, String libraryName) {
if (libraryName != null) {
Library lib = findLibrary(libraryName);
if (lib != null) {
Cell cell = lib.findNodeProto(cellName);
// Either first match in name or check that view matches
if (cell != null && (view == null || cell.getView() == view)) {
return cell;
}
}
// search in other libraries if no library is found with given name
}
for (Iterator<Library> it = Library.getLibraries(); it.hasNext();) {
Library lib = it.next();
Cell cell = lib.findNodeProto(cellName);
// Either first match in name or check that view matches
if (cell != null && (view == null || cell.getView() == view)) {
return cell;
}
}
return null;
}
/**
* Method to find the Cell with the given name in this Library.
* @param name the name of the desired Cell. It must have the view included in the name.
* @return the Cell with the given name in this Library.
*/
public Cell findNodeProto(String name) {
if (name == null) {
return null;
}
CellName n = CellName.parseName(name);
if (n == null) {
return null;
}
synchronized (cells) {
Cell cell = cells.get(n);
if (cell != null) {
return cell;
}
Cell onlyWithName = null;
for (Cell c : cells.values()) {
if (!n.getName().equals(c.getName())) {
continue;
}
// if (!n.getName().equalsIgnoreCase(c.getName())) continue;
if (onlyWithName == null || onlyWithName.getVersion() < c.getVersion()) {
onlyWithName = c;
}
if (n.getView() != c.getView()) {
continue;
}
if (n.getVersion() > 0 && n.getVersion() != c.getVersion()) {
continue;
}
if (n.getVersion() == 0 && c.getNewestVersion() != c) {
continue;
}
return c;
}
if (n.getView() == View.UNKNOWN && onlyWithName != null) {
return onlyWithName;
}
}
return null;
}
public int getNumCells() {
synchronized (cells) {
return cells.size();
}
}
/**
* Method to return an Iterator over all Cells in this Library.
* @return an Iterator over all Cells in this Library.
*/
public Iterator<Cell> getCells() {
synchronized (cells) {
ArrayList<Cell> cellsCopy = new ArrayList<Cell>(cells.values());
return cellsCopy.iterator();
}
}
/**
* Method to return an Iterator over all Cells in this Library after given CellName.
* @param cn starting CellName
* @return an Iterator over all Cells in this Library after given CellName.
*/
Iterator<Cell> getCellsTail(CellName cn) {
synchronized (cells) {
return cells.tailMap(cn).values().iterator();
}
}
/**
* Returns verison of Electric which wrote this library.
* Returns null for ReadableDumps, for new libraries and for dummy libraries.
* @return version
*/
public Version getVersion() {
return d.version;
}
/**
* Method to set library version found in header.
* @param version
*/
public void setVersion(Version version) {
setD(d.withVersion(version));
}
/**
* Returns DELIB cells.
* @return DELIB cells.
*/
public Set<CellId> getDelibCells() {
return d.delibCells;
}
/**
* Sets DELIB cells.
*/
public void setDelibCells() {
HashSet<CellId> delibCells = new HashSet<CellId>();
for (Iterator<Cell> it = getCells(); it.hasNext();) {
delibCells.add(it.next().getId());
}
setD(d.withDelibCells(delibCells));
}
}