/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LibraryFiles.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* 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.tool.io.input;
import com.sun.electric.database.IdMapper;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.id.LibId;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Setting;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.MutableTextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.lib.LibFile;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.Listener;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.io.ELIBConstants;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.ncc.basic.NccCellAnnotations;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.CircuitChangeJobs;
import com.sun.electric.tool.user.dialogs.OpenFile;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.Orientation;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class reads Library files (ELIB or readable dump) format.
*/
public abstract class LibraryFiles extends Input
{
/** key of Varible holding true library of fake cell. */ public static final Variable.Key IO_TRUE_LIBRARY = Variable.newKey("IO_true_library");
/** key of Variable to denote a dummy cell or library */ public static final Variable.Key IO_DUMMY_OBJECT = Variable.newKey("IO_dummy_object");
/** Database where to load libraries */ final EDatabase database = EDatabase.serverDatabase();
/** IdManager where to create ids */ final IdManager idManager = database.getIdManager();
/** The Library being input. */ protected Library lib;
/** true if the library is the main one being read. */ protected boolean topLevelLibrary;
// the cell information
/** The number of Cells in the file. */ protected int nodeProtoCount;
/** A list of cells being read. */ protected Cell [] nodeProtoList;
/** lambda value for each cell of the library */ protected double [] cellLambda;
/** total number of cells in all read libraries */ protected static int totalCells;
/** number of cells constructed so far. */ protected static int cellsConstructed;
/** a List of scaled Cells that got created */ protected List<Cell> scaledCells;
/** Number of errors in this LibraryFile */ protected int errorCount;
/** the Electric version in the library file. */ protected Version version;
/** true if old MOSIS CMOS technologies appear in the library */ protected boolean convertMosisCmosTechnologies;
/** true to scale lambda by 20 */ protected boolean scaleLambdaBy20;
/** true if rotation mirror bits are used */ protected boolean rotationMirrorBits;
/** SizeCorrectors for used technologies */ protected HashMap<Technology,Technology.SizeCorrector> sizeCorrectors = new HashMap<Technology,Technology.SizeCorrector>();
/** font names obtained from FONT_ASSOCIATIONS */ private String [] fontNames;
/** buffer for reading text descriptors and variable flags. */ MutableTextDescriptor mtd = new MutableTextDescriptor();
/** buffer for reading Variables. */ ArrayList<Variable> variablesBuf = new ArrayList<Variable>();
/** the path to the library being read. */ protected static String mainLibDirectory = null;
/** collection of libraries and their input objects. */ private static List<LibraryFiles> libsBeingRead;
/** collection of undefined Nodes/Technologies */ static Map<TechId, Set<PrimitiveNodeId>> undefinedTechsAndPrimitives;
/** Project preferences from library file. */ HashMap<Setting,Object> projectSettings = new HashMap<Setting,Object>();
protected static final boolean VERBOSE = false;
protected static final double TINYDISTANCE = DBMath.getEpsilon()*2;
static class NodeInstList
{
NodeInst [] theNode;
NodeProto [] protoType;
String [] name;
TextDescriptor[] nameTextDescriptor;
int [] lowX;
int [] highX;
int [] lowY;
int [] highY;
int [] anchorX;
int [] anchorY;
short [] rotation;
int [] transpose;
TextDescriptor[] protoTextDescriptor;
int [] userBits;
Variable[][] vars;
NodeInstList(int nodeCount, boolean hasAnchor)
{
theNode = new NodeInst[nodeCount];
protoType = new NodeProto[nodeCount];
name = new String[nodeCount];
nameTextDescriptor = new TextDescriptor[nodeCount];
lowX = new int[nodeCount];
highX = new int[nodeCount];
lowY = new int[nodeCount];
highY = new int[nodeCount];
if (hasAnchor) {
anchorX = new int[nodeCount];
anchorY = new int[nodeCount];
}
rotation = new short[nodeCount];
transpose = new int[nodeCount];
protoTextDescriptor = new TextDescriptor[nodeCount];
userBits = new int[nodeCount];
vars = new Variable[nodeCount][];
}
};
LibraryFiles() {}
/**
* Method to read a Library from disk.
* This method is for reading full Electric libraries in ELIB, JELIB, and Readable Dump format.
* This method doesn't read project preferences contained in library file.
* @param fileURL the URL to the disk file.
* @param libName the name to give the library (null to derive it from the file path)
* @param type the type of library file (ELIB, JELIB, etc.)
* @param quick true to read the library without verbosity (used when reading a library internally).
* @return the read Library, or null if an error occurred.
*/
public static Library readLibrary(URL fileURL, String libName, FileType type, boolean quick) {
return readLibrary(fileURL, libName, type, quick, null);
}
/**
* Method to read a Library from disk.
* This method is for reading full Electric libraries in ELIB, JELIB, and Readable Dump format.
* This method doesn't read project preferences contained in library file.
* @param fileURL the URL to the disk file.
* @param libName the name to give the library (null to derive it from the file path)
* @param type the type of library file (ELIB, JELIB, etc.)
* @param quick true to read the library without verbosity (used when reading a library internally).
* @param projectSettings an output map which is filled by project preferences of top library
* @return the read Library, or null if an error occurred.
*/
public static Library readLibrary(URL fileURL, String libName, FileType type, boolean quick,
Map<Setting,Object> projectSettings) {
if (fileURL == null) return null;
long startTime = System.currentTimeMillis();
errorLogger = ErrorLogger.newInstance("Library Read");
File f = new File(fileURL.getPath());
if (f != null && f.exists()) {
LibDirs.readLibDirs(f.getParent());
}
LibraryFiles.initializeLibraryInput();
Library lib = null;
boolean formerQuiet = isChangeQuiet();
if (!formerQuiet) changesQuiet(true);
try {
// show progress
if (!quick) startProgressDialog("library", fileURL.getFile());
Cell.setAllowCircularLibraryDependences(true);
StringBuilder errmsg = new StringBuilder();
boolean exists = TextUtils.URLExists(fileURL, errmsg);
if (!exists)
{
System.out.print(errmsg.toString());
// if doesn't have extension, assume DEFAULTLIB as extension
String fileName = fileURL.toString();
if (fileName.indexOf(".") == -1)
{
fileURL = TextUtils.makeURLToFile(fileName+"."+type.getFirstExtension());
System.out.print("Attempting to open " + fileURL+"\n");
errmsg.setLength(0);
exists = TextUtils.URLExists(fileURL, errmsg);
if (!exists && (type != FileType.DELIB)) { // Check if this is a DELIB
fileURL = TextUtils.makeURLToFile(fileName+"."+FileType.DELIB.getFirstExtension());
System.out.print("Attempting to open " + fileURL+"\n");
errmsg.setLength(0);
exists = TextUtils.URLExists(fileURL, errmsg);
if (exists) { // If it is a DELIB
libName = null; // Get the library name from fileURL
type = FileType.DELIB; // Set the type to DELIB
}
}
if (!exists) System.out.print(errmsg.toString());
}
}
if (exists)
{
// get the library name
if (libName == null) libName = TextUtils.getFileNameWithoutExtension(fileURL);
lib = readALibrary(fileURL, null, libName, type, projectSettings);
}
if (LibraryFiles.VERBOSE)
System.out.println("Done reading data for all libraries");
LibraryFiles.cleanupLibraryInput();
if (LibraryFiles.VERBOSE)
System.out.println("Done instantiating data for all libraries");
} finally {
if (!quick) stopProgressDialog();
Cell.setAllowCircularLibraryDependences(false);
}
if (!formerQuiet) changesQuiet(formerQuiet);
if (lib != null && !quick)
{
long endTime = System.currentTimeMillis();
float finalTime = (endTime - startTime) / 1000F;
System.out.println("Library " + fileURL.getFile() + " read, took " + finalTime + " seconds");
}
// if CVS is enabled, get status of library
// if (CVS.isEnabled()) {
// Update.updateOpenLibraries(Update.UpdateEnum.STATUS);
// }
errorLogger.termLogging(true);
return lib;
}
/**
* Method to read a Library from disk.
* This method is for reading full Electric libraries in ELIB, JELIB, and Readable Dump format.
* @param fileURL the URL to the disk file.
* @param type the type of library file (ELIB, JELIB, etc.)
* @return the read Library, or null if an error occurred.
*/
public static synchronized Map<String,Object> readProjectsSettingsFromLibrary(URL fileURL, FileType type)
{
if (fileURL == null) return null;
long startTime = System.currentTimeMillis();
errorLogger = ErrorLogger.newInstance("Library Read Project Preferences");
Library lib = null;
StringBuilder errmsg = new StringBuilder();
boolean exists = TextUtils.URLExists(fileURL, errmsg);
if (!exists)
{
System.out.print(errmsg.toString());
// if doesn't have extension, assume DEFAULTLIB as extension
String fileName = fileURL.toString();
if (fileName.indexOf(".") == -1)
{
fileURL = TextUtils.makeURLToFile(fileName+"."+type.getFirstExtension());
System.out.print("Attempting to open " + fileURL+"\n");
errmsg.setLength(0);
exists = TextUtils.URLExists(fileURL, errmsg);
if (!exists) {
System.out.print(errmsg.toString());
return null;
}
}
}
// handle different file types
if (type == FileType.JELIB || type == FileType.DELIB) {
TechPool techPool = EDatabase.serverDatabase().getTechPool();
try {
return settingsByXml(JELIB.readProjectSettings(fileURL, type, techPool, errorLogger));
} finally {
errorLogger.termLogging(true);
}
}
LibraryFiles in;
if (type == FileType.ELIB)
{
in = new ELIB();
if (in.openBinaryInput(fileURL)) return null;
} else if (type == FileType.READABLEDUMP)
{
in = new ReadableDump();
if (in.openTextInput(fileURL)) return null;
} else
{
System.out.println("Unknown import type: " + type);
return null;
}
// determine whether this is top-level
in.topLevelLibrary = true;
// read the library
boolean error = in.readProjectSettings();
in.closeInput();
if (error)
{
System.out.println("Error reading " + lib);
if (in.topLevelLibrary) mainLibDirectory = null;
return null;
}
long endTime = System.currentTimeMillis();
float finalTime = (endTime - startTime) / 1000F;
System.out.println("Library " + fileURL.getFile() + " read, took " + finalTime + " seconds");
errorLogger.termLogging(true);
return settingsByXml(in.projectSettings);
}
private static Map<String,Object> settingsByXml(Map<Setting,Object> settings) {
Map<String,Object> settingsByXml = new HashMap<String,Object>();
// settings is null if there were previous errros in the execution
if (settings != null)
{
for (Map.Entry<Setting,Object> e: settings.entrySet())
settingsByXml.put(e.getKey().getXmlPath(), e.getValue());
}
return settingsByXml;
}
/**
* Method to read a single library file.
* @param fileURL the URL to the file.
* @param lib the Library to read.
* If the "lib" is null, this is an entry-level library read, and one is created.
* If "lib" is not null, this is a recursive read caused by a cross-library
* reference from inside another library.
* @param type the type of library file (ELIB, CIF, GDS, etc.)
* @return the read Library, or null if an error occurred.
*/
protected static Library readALibrary(URL fileURL, Library lib, String libName, FileType type,
Map<Setting,Object> projectSettings)
{
// handle different file types
LibraryFiles in;
if (type == FileType.ELIB)
{
in = new ELIB();
if (in.openBinaryInput(fileURL)) return null;
} else if (type == FileType.JELIB || type == FileType.DELIB)
{
try {
LibId libId = lib != null ? lib.getId() : EDatabase.serverDatabase().getIdManager().newLibId(libName);
in = new JELIB(libId, fileURL, type);
} catch (IOException e) {
e.printStackTrace();
return null;
}
} else if (type == FileType.READABLEDUMP)
{
in = new ReadableDump();
if (in.openTextInput(fileURL)) return null;
} else
{
System.out.println("Unknown import type: " + type);
return null;
}
// determine whether this is top-level
in.topLevelLibrary = false;
if (lib == null)
{
mainLibDirectory = TextUtils.getFilePath(fileURL);
if (type == FileType.DELIB) {
mainLibDirectory = mainLibDirectory.replaceAll(libName+"."+type.getFirstExtension(), "");
}
in.topLevelLibrary = true;
}
if (lib == null)
{
// create a new library
lib = Library.newInstance(libName, fileURL);
}
in.lib = lib;
// read the library
boolean error = in.readInputLibrary();
in.closeInput();
if (error)
{
System.out.println("Error reading " + lib);
if (in.topLevelLibrary) mainLibDirectory = null;
return null;
}
// if (CVS.isEnabled()) {
// CVSLibrary.addLibrary(lib);
// }
if (projectSettings != null)
projectSettings.putAll(in.projectSettings);
return in.lib;
}
/**
* Reload a library from disk. Note this is different from calling
* reloadLibraryCells(List<Cell>) with a list of Cells in the library,
* because it also reloads new cells from disk that are not currently in memory.
* @param lib the Library to reload.
* @return mapping of Library/Cell/Export ids, null if the library was renamed.
*/
public static IdMapper reloadLibrary(Library lib) {
if (!lib.isFromDisk()) {
System.out.println("No disk file associated with this library, cannot reload from disk");
return null;
}
FileType type = OpenFile.getOpenFileType(lib.getLibFile().getPath(), FileType.JELIB);
// rename old library, read in disk library as replacement
// replace all cells in old library by newly read-in cells from disk library
String name = lib.getName();
URL libFile = lib.getLibFile();
boolean isCurrent = Library.getCurrent() == lib;
IdMapper idMapper = lib.setName("___old___"+name);
if (idMapper == null) return null;
lib = idMapper.get(lib.getId()).inDatabase(EDatabase.serverDatabase());
lib.setHidden(); // hide the old library
startProgressDialog("library", name);
Library newLib = readLibrary(libFile, name, type, true);
stopProgressDialog();
if (isCurrent)
Job.setCurrentLibraryInJob(newLib);
// replace all old cells with new cells
Cell.setAllowCircularLibraryDependences(true);
for (Iterator<Cell> it = lib.getCells(); it.hasNext(); ) {
Cell oldCell = it.next();
String cellName = oldCell.getCellName().toString();
Cell newCell = newLib.findNodeProto(cellName);
if (newCell == null) {
// doesn't exist in new library, copy it over
System.out.println("Warning, Cell "+oldCell.describe(false)+" does not exist in reloaded library. Copying it to reloaded library.");
newCell = Cell.copyNodeProto(oldCell, newLib, cellName, true);
}
// replace all usages of old cell with new cell
List<NodeInst> instances = new ArrayList<NodeInst>();
for(Iterator<NodeInst> it2 = oldCell.getInstancesOf(); it2.hasNext(); ) {
instances.add(it2.next());
}
for (NodeInst ni : instances) {
CircuitChangeJobs.replaceNodeInst(ni, newCell, true, true);
}
}
Cell.setAllowCircularLibraryDependences(false);
// close temp library
lib.kill("delete old library");
System.out.println("Reloaded library "+newLib.getName()+" from disk.");
return idMapper;
}
/**
* Reload Cells from disk. Other cells in the library will not be affected.
* These cells must all be from the same library.
*/
// public static void reloadLibraryCells(List<Cell> cellList) {
// if (cellList == null || cellList.size() == 0) return;
// Library lib = cellList.get(0).getLibrary();
//
// if (!lib.isFromDisk()) {
// System.out.println("No disk file associated with this library, cannot reload from disk");
// return;
// }
// FileType type = OpenFile.getOpenFileType(lib.getLibFile().getPath(), FileType.JELIB);
//
// // Load disk library as temp library.
// // Then, copy over new cells from temp library to replace current cells in memory,
// // then close temp library.
//
// String name = lib.getName();
// URL libFile = lib.getLibFile();
// startProgressDialog("library", name);
// Library newLib = readLibrary(libFile, "___reloaded___"+name, type, true, iconParams);
// stopProgressDialog();
// newLib.setHidden();
//
// // replace all old cells with new cells
// Cell.setAllowCircularLibraryDependences(true);
// for (Cell oldCell : cellList) {
// String cellName = oldCell.getCellName().toString(); // contains version and view info
//
// Cell newCell = newLib.findNodeProto(cellName);
// if (newCell == null) {
// System.out.println("Cell "+oldCell.describe(false)+" cannot be reloaded, it does not exist in disk library");
// continue;
// }
//
// // rename oldCell
// String renamedName = "___old___"+oldCell.getName();
// IdMapper idMapper = oldCell.rename(renamedName, renamedName); // must not contain version or view info
// if (idMapper == null) continue;
// oldCell = idMapper.get(oldCell.getId()).inDatabase(EDatabase.serverDatabase());
//
// // copy newCell over with oldCell's name
// newCell = Cell.copyNodeProto(newCell, lib, cellName, true);
//
// // replace all usages of old cell with new cell
// List<NodeInst> instances = new ArrayList<NodeInst>();
// for(Iterator<NodeInst> it2 = oldCell.getInstancesOf(); it2.hasNext(); ) {
// instances.add(it2.next());
// }
// for (NodeInst ni : instances) {
// CircuitChangeJobs.replaceNodeInst(ni, newCell, true, true);
// }
//
// // kill oldCell
// oldCell.kill();
// }
// Cell.setAllowCircularLibraryDependences(false);
//
// // close temp library
// newLib.kill("delete temp library");
// System.out.println("Reloaded Cells from disk.");
// }
// *************************** THE CREATION INTERFACE ***************************
public static void initializeLibraryInput()
{
libsBeingRead = new ArrayList<LibraryFiles>();
undefinedTechsAndPrimitives = new HashMap<TechId, Set<PrimitiveNodeId>>();
}
public boolean readInputLibrary()
{
// add this reader to the list
assert(!libsBeingRead.contains(this));
libsBeingRead.add(this);
//libsBeingRead.put(lib, this);
scaledCells = new ArrayList<Cell>();
try
{
if (readTheLibrary(false, null))
return true;
Map<Cell,Variable[]> originalVars = createLibraryCells(false);
realizeCellVariables(originalVars);
return false;
} catch (IOException e)
{
System.out.println("End of file reached while reading " + filePath);
return true;
}
}
abstract boolean readTheLibrary(boolean onlyProjectSettings, LibraryStatistics.FileContents fc) throws IOException;
abstract Map<Cell,Variable[]> createLibraryCells(boolean onlyProjectSettings);
private void realizeCellVariables(Map<Cell,Variable[]> originalVars) {
HashSet<Cell.CellGroup> visitedCellGroups = new HashSet<Cell.CellGroup>();
for (Cell cell: originalVars.keySet()) {
Cell.CellGroup cellGroup = cell.getCellGroup();
if (visitedCellGroups.contains(cellGroup)) continue;
realizeCellVariables(cellGroup, originalVars);
visitedCellGroups.add(cellGroup);
}
}
/**
* Realize cell variables on all cells of a cell group
* @param cellGroup the cell group to realize variables
* @param originalVars original variables from disk library
*/
private void realizeCellVariables(Cell.CellGroup cellGroup, Map<Cell,Variable[]> originalVars) {
// Determine and create group parameters
Cell cellOwner = cellGroup.getParameterOwner();
HashMap<Variable.AttrKey,Variable> groupParams = null;
if (cellOwner != null) {
groupParams = new HashMap<Variable.AttrKey,Variable>();
Variable[] ownerVars = originalVars.get(cellOwner);
for (Variable var: ownerVars) {
if (!var.getTextDescriptor().isParam()) continue;
if (cellOwner.isDeprecatedVariable(var.getKey())) continue;
if (var.getKey() == NccCellAnnotations.NCC_ANNOTATION_KEY) continue;
var = var.withInherit(true);
cellGroup.addParam(var);
groupParams.put((Variable.AttrKey)var.getKey(), var);
}
}
/**
* Modify attributes of group parameters in each icon and schematic cell
* Realize variables
*/
for (Iterator<Cell> it = cellGroup.getCells(); it.hasNext(); ) {
Cell cell = it.next();
Variable[] origVars = originalVars.get(cell);
if (cell.isIcon() || cell.isSchematic())
realizeCellVariables(cell, origVars, groupParams);
else
realizeVariables(cell, origVars);
}
}
private void realizeCellVariables(Cell cell, Variable[] origVars, HashMap<Variable.AttrKey,Variable> groupParams) {
int foundParams = 0;
for (Variable var: origVars) {
if (var.isAttribute()) {
Variable.Key varKey = var.getKey();
Variable groupParam = groupParams.get(varKey);
if (groupParam != null) {
if (!var.getTextDescriptor().isParam()) {
String msg = "Attribute \"" + var.getTrueName() + "\" on " + cell +
" must be parameter as on " + cell.getCellGroup().getParameterOwner();
errorLogger.logError(msg, EPoint.fromLambda(var.getXOff(), var.getYOff()), cell, 2);
var = var.withParam(true);
}
var = var.withInherit(true);
if (!var.getObject().equals(groupParam.getObject())) {
String msg = "Parameter \"" + var.getTrueName() +
"\" has value " + var.getPureValue(-1) + " on " + cell +
" instead of " + groupParam.getPureValue(-1) + " as on " + cell.getCellGroup().getParameterOwner();
errorLogger.logError(msg, EPoint.fromLambda(var.getXOff(), var.getYOff()), cell, 2);
var = var.withObject(groupParam.getObject());
}
if (var.getUnit() != groupParam.getUnit()) {
String msg = "Parameter \"" + var.getTrueName() +
"\" has unit " + var.getUnit() + " on " + cell +
" instead of " + groupParam.getUnit() + " as on " + cell.getCellGroup().getParameterOwner();
errorLogger.logError(msg, EPoint.fromLambda(var.getXOff(), var.getYOff()), cell, 2);
var = var.withUnit(groupParam.getUnit());
}
foundParams++;
cell.setTextDescriptor(var.getKey(), var.getTextDescriptor());
continue;
}
if (var.getTextDescriptor().isParam()) {
String msg = cell + " has extra parameter \"" + var.getTrueName() +
"\" compared to " + cell.getCellGroup().getParameterOwner();
errorLogger.logError(msg, EPoint.fromLambda(var.getXOff(), var.getYOff()), cell, 2);
var = var.withParam(false);
if (var.getKey() == NccCellAnnotations.NCC_ANNOTATION_KEY)
var = var.withInherit(false).withInterior(false);
}
}
realizeVariable(cell, var);
}
if (foundParams == groupParams.size()) return;
// Create parameters which were omitted in the disk library
HashSet<Variable.AttrKey> omittedParams = new HashSet<Variable.AttrKey>(groupParams.keySet());
for (Variable var: origVars)
omittedParams.remove(var.getKey());
assert omittedParams.size() + foundParams == groupParams.size();
// For icon cell try to find variables on an example icon in schematic cell
Variable[] exampleVars = null;
if (cell.isIcon()) {
for (Iterator<Cell> cit = cell.getCellGroup().getCells(); cit.hasNext();) {
Cell schCell = cit.next();
if (!schCell.isSchematic()) continue;
exampleVars = findVarsOnExampleIcon(schCell, cell);
if (exampleVars == null) continue;
break;
}
}
for (Variable.AttrKey omittedParam: omittedParams) {
Variable param = groupParams.get(omittedParam);
String msg = cell + " must have parameter \"" + param.getTrueName() + "\" as on " + cell.getCellGroup().getParameterOwner();
Variable exampleParam = null;
if (exampleVars != null) {
for (Variable var: exampleVars) {
if (var == null || var.getKey() != omittedParam) continue;
exampleParam = var;
break;
}
}
if (exampleParam != null) {
TextDescriptor td = exampleParam.getTextDescriptor();
td = td.withInherit(true).withParam(true).withUnit(param.getUnit());
boolean interior = !exampleParam.isDisplay();
td = td.withInterior(interior).withDisplay(true);
param = param.withTextDescriptor(td);
}
assert param.getTextDescriptor().isParam() && param.isInherit();
errorLogger.logError(msg, EPoint.fromLambda(param.getXOff(), param.getYOff()), cell, 2);
cell.setTextDescriptor(param.getKey(), param.getTextDescriptor());
}
}
abstract Variable[] findVarsOnExampleIcon(Cell parentCell, Cell iconCell);
protected void scanNodesForRecursion(Cell cell, HashSet<Cell> markCellForNodes, HashSet<Cell> patchedCells, NodeProto [] nil, int start, int end)
{
// scan the nodes in this cell and recurse
for(int j=start; j<end; j++)
{
NodeProto np = nil[j];
if (np instanceof PrimitiveNode) continue;
Cell otherCell = (Cell)np;
if (otherCell == null) continue;
// subcell: make sure that cell is setup
if (markCellForNodes.contains(otherCell)) continue;
LibraryFiles reader = this;
if (otherCell.getLibrary() != cell.getLibrary())
reader = getReaderForLib(otherCell.getLibrary());
// subcell: make sure that cell is setup
if (reader != null)
reader.realizeCellsRecursively(otherCell, markCellForNodes, patchedCells, null, 0);
}
markCellForNodes.add(cell);
}
/**
* Method to read project preferences from a Library.
* This method is never called.
* Instead, it is always overridden by the appropriate read subclass.
* @return true on error.
*/
protected boolean readProjectSettings() { return true; }
/**
* Method to find the View to use for an old View name.
* @param viewName the old View name.
* @return the View to use (null if not found).
*/
protected View findOldViewName(String viewName)
{
if (version.getMajor() < 8)
{
if (viewName.equals("compensated")) return View.LAYOUTCOMP;
if (viewName.equals("skeleton")) return View.LAYOUTSKEL;
if (viewName.equals("simulation-snapshot")) return View.DOCWAVE;
if (viewName.equals("netlist-netlisp-format")) return View.NETLISTNETLISP;
if (viewName.equals("netlist-rsim-format")) return View.NETLISTRSIM;
if (viewName.equals("netlist-silos-format")) return View.NETLISTSILOS;
if (viewName.equals("netlist-quisc-format")) return View.NETLISTQUISC;
if (viewName.equals("netlist-als-format")) return View.NETLISTALS;
}
return null;
}
protected Technology findTechnologyName(String name)
{
Technology tech = null;
if (convertMosisCmosTechnologies)
{
if (name.equals("mocmossub")) tech = Technology.getMocmosTechnology(); else
if (name.equals("mocmos")) tech = Technology.findTechnology("mocmosold");
}
if (tech == null) tech = Technology.findTechnology(name);
if (tech == null && name.equals("logic"))
tech = Schematics.tech();
if (tech == null && (name.equals("epic8c") || name.equals("epic7c")))
tech = Technology.findTechnology("epic7s");
return tech;
}
/**
* Method to read an external library file, given its name as stored on disk.
* Attempts to find the file in many different ways, including asking the user.
* @param theFileName the full path to the file, as written to disk.
* @return a Library that was read. If library cannot be read or found, creates
* a Library called DUMMYname, and returns that.
*/
protected Library readExternalLibraryFromFilename(String theFileName, FileType defaultType)
{
// get the path to the library file
String legalLibName = TextUtils.getFileNameWithoutExtension(theFileName, true);
// Checking if the library is already open
Library elib = Library.findLibrary(legalLibName);
if (elib != null) return elib;
URL externalURL = searchExternalLibraryFromFilename(mainLibDirectory, theFileName, defaultType);
boolean exists = (externalURL != null);
// last option: let user pick library location
if (!exists)
{
String pt = null;
while (true) {
// continue to ask the user where the library is until they hit "cancel"
String description = "Reference library '" + theFileName + "'";
pt = OpenFile.chooseInputFile(FileType.LIBFILE, description);
if (pt == null) {
// user cancelled, break
break;
}
// see if user chose a file we can read
externalURL = TextUtils.makeURLToFile(pt);
if (externalURL != null) {
exists = TextUtils.URLExists(externalURL, null);
if (exists) {
// good pt, opened it, get out of here
break;
}
}
}
}
if (exists)
{
System.out.println("Reading referenced library " + externalURL.getFile());
elib = Library.newInstance(legalLibName, externalURL);
}
if (elib != null)
{
// read the external library
String oldNote = getProgressNote();
setProgressValue(0);
setProgressNote("Reading referenced library " + legalLibName + "...");
// get the library name
FileType importType = OpenFile.getOpenFileType(externalURL.getFile(), defaultType);
String eLibName = TextUtils.getFileNameWithoutExtension(externalURL);
elib = readALibrary(externalURL, elib, eLibName, importType, null);
setProgressValue(100);
setProgressNote(oldNote);
}
if (elib == null)
{
System.out.println("Error: cannot find referenced library " + theFileName);
System.out.println("...Creating new "+legalLibName+" Library instead");
elib = Library.newInstance(legalLibName, null);
elib.setLibFile(TextUtils.makeURLToFile(theFileName));
elib.clearFromDisk();
}
// if (failed) elib->userbits |= UNWANTEDLIB; else
// {
// // queue this library for announcement through change control
// io_queuereadlibraryannouncement(elib);
// }
return elib;
}
/**
* Method to search an external library file, given its name as stored on disk.
* Attempts to find the file in many different ways, including asking the user.
* @param theFileName the full path to the file, as written to disk.
* @param defaultType default file type
* @return an URL to Library file.
*/
public static URL searchExternalLibraryFromFilename(String mainLibDirectory, String theFileName, FileType defaultType) {
// see if this library is already read in
String libFileName = theFileName;
// special case if the library path came from a different computer system and still has separators
while (libFileName.endsWith("\\") || libFileName.endsWith(":") || libFileName.endsWith("/"))
libFileName = libFileName.substring(0, libFileName.length()-1);
int backSlashPos = libFileName.lastIndexOf('\\');
int colonPos = libFileName.lastIndexOf(':');
int slashPos = libFileName.lastIndexOf('/');
int charPos = Math.max(backSlashPos, Math.max(colonPos, slashPos));
if (charPos >= 0)
{
libFileName = libFileName.substring(charPos+1);
}
String libName = libFileName;
FileType importType = OpenFile.getOpenFileType(libName, defaultType);
FileType preferredType = importType;
// this is just to remove the extension from the lib name string
if (libName.endsWith(".elib"))
{
libName = libName.substring(0, libName.length()-5);
} else if (libName.endsWith(".jelib"))
{
libName = libName.substring(0, libName.length()-6);
} else if (libName.endsWith(".delib"))
{
libName = libName.substring(0, libName.length()-6);
} else if (libName.endsWith(".txt"))
{
libName = libName.substring(0, libName.length()-4);
} else
{
// no recognizable extension, add one to the file name
libFileName += "." + defaultType.getFirstExtension();
}
StringBuilder errmsg = new StringBuilder();
// first try the library name with the extension it came with
// However, do not look in electric library area to avoid problems with spice configurations for old chips
URL externalURL = getLibrary(mainLibDirectory, libName + "." + preferredType.getFirstExtension(), theFileName, errmsg, true);
// Now try all file types, starting with jelib
// try JELIB
if (externalURL == null && preferredType != FileType.JELIB) {
externalURL = getLibrary(mainLibDirectory, libName + "." + FileType.JELIB.getFirstExtension(), theFileName, errmsg, true);
}
// try ELIB
if (externalURL == null && preferredType != FileType.ELIB) {
externalURL = getLibrary(mainLibDirectory, libName + "." + FileType.ELIB.getFirstExtension(), theFileName, errmsg, true);
}
// try DELIB
if (externalURL == null && preferredType != FileType.DELIB) {
externalURL = getLibrary(mainLibDirectory, libName + "." + FileType.DELIB.getFirstExtension(), theFileName, errmsg, true);
}
// try txt
if (externalURL == null && preferredType != FileType.READABLEDUMP) {
externalURL = getLibrary(mainLibDirectory, libName + "." + FileType.READABLEDUMP.getFirstExtension(), theFileName, errmsg, true);
}
if (externalURL == null) {
System.out.println("Error: cannot find referenced library " + libName+":");
System.out.print(errmsg.toString());
}
return externalURL;
}
/** Get the URL to the library named libFileName
* @param libFileName the library file name (with extension)
* @param originalPath the original, exact path of the reference if any
* @param errmsg a StringBuilder into which errors may be placed.
* @param checkElectricLib to force search in Electric library area
* @return null if not found, or valid URL if file found
*/
private static URL getLibrary(String mainLibDirectory, String libFileName, String originalPath, StringBuilder errmsg, boolean checkElectricLib)
{
// library does not exist: see if file is in the same directory as the main file
URL firstURL = TextUtils.makeURLToFile(mainLibDirectory + libFileName);
boolean exists = TextUtils.URLExists(firstURL, errmsg);
if (exists) return firstURL;
HashMap<String,String> searchedURLs = new HashMap<String,String>();
// try secondary library file locations
for (Iterator<String> libIt = LibDirs.getLibDirs(); libIt.hasNext(); )
{
URL url = TextUtils.makeURLToFile(libIt.next() + File.separator + libFileName);
exists = TextUtils.URLExists(url, errmsg);
if (exists) return url;
if (url != null) searchedURLs.put(url.getFile(), url.getFile());
}
// check the current working dir
// (Note that this is not necessarily the same as the mainLibDirectory above)
// Do NOT search User.getCurrentWorkingDir, as another Electric process can
// modify that during library read instead, search System.getProperty("user.dir");
URL thirdURL = TextUtils.makeURLToFile(System.getProperty("user.dir") + File.separator + libFileName);
if (thirdURL != null && !searchedURLs.containsKey(thirdURL.getFile()))
{
exists = TextUtils.URLExists(thirdURL, errmsg);
if (exists) return thirdURL;
if (thirdURL != null) searchedURLs.put(thirdURL.getFile(), thirdURL.getFile());
}
// try the exact path specified in the reference
if (originalPath != null) {
URL url = TextUtils.makeURLToFile(originalPath);
String fileName = url.getFile();
File libFile = new File(fileName);
originalPath = libFile.getParent();
URL secondURL = TextUtils.makeURLToFile(originalPath + File.separator + libFileName);
if (secondURL != null && !searchedURLs.containsKey(secondURL.getFile()))
{
exists = TextUtils.URLExists(secondURL, errmsg);
if (exists) return secondURL;
if (secondURL != null) searchedURLs.put(secondURL.getFile(), secondURL.getFile());
}
}
if (checkElectricLib)
{
// try the Electric library area
URL url = LibFile.getLibFile(libFileName);
exists = TextUtils.URLExists(url, errmsg);
if (exists) return url;
}
return null;
}
public static void cleanupLibraryInput()
{
setProgressValue(0);
setProgressNote("Constructing cell contents...");
// Compute technology of new cells
Set<Cell> uncomputedCells = new HashSet<Cell>();
for(LibraryFiles reader : libsBeingRead)
{
for(int cellIndex=0; cellIndex<reader.nodeProtoCount; cellIndex++)
{
Cell cell = reader.nodeProtoList[cellIndex];
if (cell == null) continue;
if (cell.getLibrary() != reader.lib) continue;
uncomputedCells.add(cell);
}
}
for(LibraryFiles reader : libsBeingRead)
{
for(int cellIndex=0; cellIndex<reader.nodeProtoCount; cellIndex++)
{
Cell cell = reader.nodeProtoList[cellIndex];
if (cell == null) continue;
if (cell.getLibrary() != reader.lib) continue;
reader.computeTech(cell, uncomputedCells);
}
}
// clear flag bits for scanning the library hierarchically
totalCells = 0;
HashSet<Cell> markCellForNodes = new HashSet<Cell>();
for(LibraryFiles reader : libsBeingRead)
{
totalCells += reader.nodeProtoCount;
for(int cellIndex=0; cellIndex<reader.nodeProtoCount; cellIndex++)
{
Cell cell = reader.nodeProtoList[cellIndex];
if (cell == null) continue;
if (cell.getLibrary() != reader.lib) continue;
reader.cellLambda[cellIndex] = reader.computeLambda(cell, cellIndex);
cell.setTempInt(cellIndex);
}
}
cellsConstructed = 0;
// now recursively adjust lambda sizes
if (LibraryFiles.VERBOSE)
System.out.println("Preparing to compute scale factors");
for(int i=0; i<20; i++)
{
boolean unchanged = true;
for(LibraryFiles reader : libsBeingRead)
{
for(int cellIndex=0; cellIndex<reader.nodeProtoCount; cellIndex++)
{
Cell cell = reader.nodeProtoList[cellIndex];
if (cell == null) continue;
if (cell.getLibrary() != reader.lib) continue;
if (reader.spreadLambda(cell, cellIndex))
{
unchanged = false;
}
}
}
if (unchanged) break;
}
if (LibraryFiles.VERBOSE)
System.out.println("Finished computing scale factors");
// recursively create the cell contents
HashSet<Cell> patchedCells = new HashSet<Cell>();
for(LibraryFiles reader : libsBeingRead)
{
for(int cellIndex=0; cellIndex<reader.nodeProtoCount; cellIndex++)
{
Cell cell = reader.nodeProtoList[cellIndex];
if (cell == null) continue;
if (markCellForNodes.contains(cell)) continue;
reader.realizeCellsRecursively(cell, markCellForNodes, patchedCells, null, 0);
}
}
for (LibraryFiles reader: libsBeingRead)
reader.lib.clearChanged(patchedCells);
// tell which libraries had extra "scaled" cells added
boolean first = true;
for(LibraryFiles reader : libsBeingRead)
{
if (reader.scaledCells != null && reader.scaledCells.size() != 0)
{
if (first)
{
System.out.println("WARNING: to accommodate scaling inconsistencies, these cells were created:");
first = false;
}
StringBuffer sb = new StringBuffer();
sb.append(" Library " + reader.lib.getName() + ":");
for(Cell cell : reader.scaledCells)
{
sb.append(" " + cell.noLibDescribe());
}
System.out.println(sb.toString());
}
}
// adjust for old library conversion
// convertOldLibraries();
// broadcast the library-read to all listeners
for(Iterator<Listener> it = Tool.getListeners(); it.hasNext(); )
{
Listener listener = it.next();
for(LibraryFiles reader : libsBeingRead)
{
listener.readLibrary(reader.lib);
}
}
if (!undefinedTechsAndPrimitives.isEmpty()) {
String prims = "";
for (TechId techId: undefinedTechsAndPrimitives.keySet())
{
int count = 0;
for (PrimitiveNodeId primId : undefinedTechsAndPrimitives.get(techId))
{
prims += " " + primId + ", ";
count += primId.name.length();
if (count > 50) // 50 is an arbritary number
{
count = 0;
prims += "\n";
}
}
Job.getUserInterface().showErrorMessage("Library contains unknown nodes from the technology '" + techId + "':\n" + prims,
"Unknown nodes/technologies");
}
}
// clean up init (free LibraryFiles for garbage collection)
libsBeingRead.clear();
undefinedTechsAndPrimitives.clear();
}
// private static void convertOldLibraries()
// {
// // see if the MOSIS CMOS technology now has old-style state information
// MoCMOS.tech.convertOldState();
// }
protected LibraryFiles getReaderForLib(Library lib) {
for (LibraryFiles reader : libsBeingRead) {
if (reader.lib == lib) return reader;
}
return null;
}
Technology.SizeCorrector getSizeCorrector(Technology tech) {
Technology.SizeCorrector corrector = sizeCorrectors.get(tech);
if (corrector == null) {
corrector = tech.getSizeCorrector(version, projectSettings, false, false);
sizeCorrectors.put(tech, corrector);
}
return corrector;
}
String convertCellName(String s)
{
StringBuffer buf = null;
for (int i = 0; i < s.length(); i++)
{
char ch = s.charAt(i);
if (ch == '\n' || ch == '|' || ch == ':')
{
if (buf == null)
{
buf = new StringBuffer();
buf.append(s.substring(0, i));
}
buf.append('-');
continue;
} else if (buf != null)
{
buf.append(ch);
}
}
if (buf != null)
{
String newS = buf.toString();
System.out.println("Cell name " + s + " was converted to " + newS);
return newS;
}
return s;
}
/**
* Method to conver name of Geometric object.
* @param value name of object
* @param isDisplay true if this is displayable variable.
*/
protected static String convertGeomName(Object value, boolean isDisplay)
{
if (value == null || !(value instanceof String)) return null;
String str = (String)value;
int indexOfAt = str.indexOf('@');
if (isDisplay)
{
if (indexOfAt >= 0)
{
String newS = "";
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (c == '@') c = '_';
newS += c;
}
str = newS;
}
} else if (indexOfAt < 0) return null;
return str;
}
/**
* Method to build a NodeInst.
* @param nil arrays with data of new NodeInst
* @param nodeIndex index in nik array
* @param xoff x-offset of NodeInst in integer coordinates
* @param yoff y-offset of NodeInst in integer coordinates
* @param lambda scale factor
* @param parent parent Cell
* @param proto actual node proto (may be different for instance of scaled Cells)
*/
void realizeNode(NodeInstList nil, int nodeIndex, int xoff, int yoff, double lambda, Cell parent, NodeProto proto)
{
double lowX = nil.lowX[nodeIndex]-xoff;
double lowY = nil.lowY[nodeIndex]-yoff;
double highX = nil.highX[nodeIndex]-xoff;
double highY = nil.highY[nodeIndex]-yoff;
Point2D center = new Point2D.Double(((lowX + highX) / 2) / lambda, ((lowY + highY) / 2) / lambda);
EPoint size = EPoint.ORIGIN;
if (proto instanceof PrimitiveNode) {
PrimitiveNode pn = (PrimitiveNode)proto;
size = getSizeCorrector(pn.getTechnology()).getSizeFromDisk(pn, (highX - lowX) / lambda, (highY - lowY) / lambda);
}
int rotation = nil.rotation[nodeIndex];
boolean flipX = false;
boolean flipY = false;
if (rotationMirrorBits)
{
// new version: allow mirror bits
if ((nil.transpose[nodeIndex]&1) != 0)
{
flipY = true;
rotation = (rotation + 900) % 3600;
}
if ((nil.transpose[nodeIndex]&2) != 0)
{
// mirror in X
flipX = true;
}
if ((nil.transpose[nodeIndex]&4) != 0)
{
// mirror in Y
flipY = !flipY;
}
} else
{
// old version: just use transpose information
if (nil.transpose[nodeIndex] != 0)
{
flipY = true;
rotation = (rotation + 900) % 3600;
}
}
Orientation orient = Orientation.fromJava(rotation, flipX, flipY);
// figure out the grab center if this is a cell instance
if (proto instanceof Cell)
{
if (nil.anchorX != null)
// if (magic <= ELIBConstants.MAGIC13)
{
// version 13 and later stores and uses anchor location
double anchorX = (nil.anchorX[nodeIndex]-xoff) / lambda;
double anchorY = (nil.anchorY[nodeIndex]-yoff) / lambda;
center.setLocation(anchorX, anchorY);
} else
{
Cell subCell = (Cell)proto;
Rectangle2D bounds = subCell.getBounds();
Point2D shift = new Point2D.Double(-bounds.getCenterX(), -bounds.getCenterY());
AffineTransform trans = orient.pureRotate();
trans.transform(shift, shift);
center.setLocation(center.getX() + shift.getX(), center.getY() + shift.getY());
}
}
int flags = ImmutableNodeInst.flagsFromElib(nil.userBits[nodeIndex]);
int techBits = ImmutableNodeInst.techSpecificFromElib(nil.userBits[nodeIndex]);
Integer cutAlignment = null;
if (proto instanceof PrimitiveNode && ((PrimitiveNode)proto).isMulticut() && techBits != 0) {
cutAlignment = Integer.valueOf(techBits);
techBits = 0;
}
NodeInst ni = NodeInst.newInstance(parent, proto, nil.name[nodeIndex], nil.nameTextDescriptor[nodeIndex],
center, size, orient, flags, techBits, nil.protoTextDescriptor[nodeIndex], Input.errorLogger);
nil.theNode[nodeIndex] = ni;
// if (proto instanceof PrimitiveNode) {
// PrimitiveNode pn = (PrimitiveNode)proto;
// PrimitiveNode.NodeSizeRule nodeSizeRule = pn.getMinSizeRule();
// if (nodeSizeRule != null) {
// if (size.getLambdaX() < nodeSizeRule.getWidth())
// Input.errorLogger.logWarning(" (" + parent + ") node " + ni.getName() + " width is less than minimum by " +
// (nodeSizeRule.getWidth() - size.getLambdaX()), ni, parent, null, 2);
// if (size.getLambdaY() < nodeSizeRule.getHeight())
// Input.errorLogger.logWarning(" (" + parent + ") node " + ni.getName() + " height is less than minimum by " +
// (nodeSizeRule.getHeight() - size.getLambdaY()), ni, parent, null, 2);
// }
// }
if (ni == null) return;
Variable[] vars = nil.vars[nodeIndex];
if (vars != null) {
for (int j = 0; j < vars.length; j++) {
Variable var = vars[j];
if (var == null) continue;
// convert outline information
if (var.getKey() == NodeInst.TRACE && proto instanceof PrimitiveNode && ((PrimitiveNode)proto).isHoldsOutline() ) {
Object value = var.getObject();
if (value instanceof Integer[] || value instanceof Float[]) {
// convert outline information, if present
Number[] outline = (Number[])value;
int newLength = outline.length / 2;
EPoint [] newOutline = new EPoint[newLength];
double lam = outline instanceof Integer[] ? lambda : 1.0;
for(int k=0; k<newLength; k++) {
double oldX = outline[k*2].doubleValue()/lam;
double oldY = outline[k*2+1].doubleValue()/lam;
if (!Double.isNaN(oldX) && !Double.isNaN(oldY))
newOutline[k] = new EPoint(oldX, oldY);
}
var = var.withObject(newOutline);
}
}
if (ni.isDeprecatedVariable(var.getKey())) continue;
if (ni.isParam(var.getKey()))
ni.addParameter(var);
else
ni.addVar(var.withParam(false));
}
}
if (cutAlignment != null)
ni.newVar(Technology.NodeLayer.CUT_ALIGNMENT, cutAlignment);
// if this was a dummy cell, log instance as an error so the user can find easily
if (proto instanceof Cell && ((Cell)proto).getVar(IO_DUMMY_OBJECT) != null) {
Input.errorLogger.logError("Instance of dummy cell "+proto.getName(), ni, parent, null, 1);
}
}
void realizeVariables(ElectricObject eObj, Variable[] vars) {
if (vars == null) return;
for (Variable var: vars)
realizeVariable(eObj, var);
}
private void realizeVariable(ElectricObject eObj, Variable var) {
if (var == null || eObj.isDeprecatedVariable(var.getKey())) return;
if (eObj.isParam(var.getKey()) && eObj instanceof NodeInst) {
((NodeInst)eObj).addParameter(var);
return;
}
var = var.withParam(false);
String origVarName = var.getKey().toString();
// convert old port variables
if (eObj instanceof NodeInst && var.getKey().getName().startsWith("ATTRP_")) {
NodeInst ni = (NodeInst)eObj;
// the form is "ATTRP_portName_variableName" with "\" escapes
StringBuffer portName = new StringBuffer();
String varName = null;
int len = origVarName.length();
for(int j=6; j<len; j++) {
char ch = origVarName.charAt(j);
if (ch == '\\') {
j++;
portName.append(origVarName.charAt(j));
continue;
}
if (ch == '_') {
varName = origVarName.substring(j+1);
break;
}
portName.append(ch);
}
if (varName != null) {
String thePortName = portName.toString();
PortProto pp = findPortProto(ni.getProto(), thePortName);
PortInst pi = pp != null ? ni.findPortInstFromProto(pp) : null;
if (pi != null) {
pi.newVar(Variable.newKey(varName), var.getObject(), var.getTextDescriptor());
return;
}
}
}
eObj.addVar(var);
}
static PortProto findPortProto(NodeProto np, String portId) {
PortProtoId portProtoId = np.getId().newPortId(portId);
PortProto pp = np.getPort(portProtoId);
if (pp != null) return pp;
if (np.getNumPorts() == 1 && portId.length() == 0)
return np.getPort(0);
if (np instanceof PrimitiveNode) {
PrimitiveNode primNode = (PrimitiveNode)np;
pp = primNode.findPortProto(portId);
if (pp == null)
pp = primNode.getTechnology().convertOldPortName(portId, primNode);
}
return pp;
}
/**
* Method to add meaning preferences to an ElectricObject from a List of strings.
* @param obj the Object to augment with meaning preferences.
* @param vars Variables with meaning preferences.
*/
void realizeMeaningPrefs(Object obj, Variable[] vars) {
realizeMeaningPrefs(projectSettings, obj, vars);
}
/**
* Method to add meaning preferences to an ElectricObject from a List of strings.
* @param projectSettings a map for result
* @param obj the Object to augment with meaning preferences.
* @param vars Variables with meaning preferences.
*/
static void realizeMeaningPrefs(HashMap<Setting,Object> projectSettings, Object obj, Variable[] vars) {
HashMap<String,Object> settingsByPrefName = new HashMap<String,Object>();
for (int i = 0; i < vars.length; i++) {
Variable var = vars[i];
if (var == null) continue;
Object value = var.getObject();
if (!(value instanceof String)) {
if (value instanceof Short || value instanceof Byte)
value = new Integer(((Number)value).intValue());
if (!(value instanceof Number) && !(value instanceof Boolean))
continue;
}
String prefName = var.getKey().getName();
String prefPath = null;
if (obj instanceof Technology) {
prefPath = Technology.TECH_NODE + "/";
Map<Setting,Object> convertedVars = ((Technology)obj).convertOldVariable(prefName, value);
if (convertedVars != null) {
for (Map.Entry<Setting,Object> e: convertedVars.entrySet()) {
Setting setting = e.getKey();
prefName = setting.getPrefName();
value = e.getValue();
projectSettings.put(setting, value);
}
continue;
}
} else if (obj instanceof Tool) {
prefPath = ((Tool)obj).prefs.relativePath() + "/";
}
settingsByPrefName.put(prefPath + prefName, value);
}
Setting.Group grp = obj instanceof Technology ? ((Technology)obj).getProjectSettings() : ((Tool)obj).getProjectSettings();
for (Setting setting: grp.getSettings()) {
Object value = settingsByPrefName.get(setting.getPrefPath());
if (value != null)
projectSettings.put(setting, value);
}
}
TextDescriptor makeDescriptor(int td0, int td1) {
mtd.setCBits(td0, fixTextDescriptorFont(td1));
return TextDescriptor.newTextDescriptor(mtd);
}
TextDescriptor makeDescriptor(int td0, int td1, int flags) {
mtd.setCBits(td0, fixTextDescriptorFont(td1), flags);
return TextDescriptor.newTextDescriptor(mtd);
}
/**
* Method to grab font associations that were stored on a Library.
* The font associations are used later to convert indices to true font names and numbers.
* @param associationArray array from FONT_ASSOCIATIONS variable.
*/
void setFontNames(String[] associationArray)
{
int maxAssociation = 0;
for(int i=0; i<associationArray.length; i++)
{
if (associationArray[i] == null) continue;
int fontNumber = TextUtils.atoi(associationArray[i]);
if (fontNumber > maxAssociation) maxAssociation = fontNumber;
}
if (maxAssociation <= 0) return;
fontNames = new String[maxAssociation];
for(int i=0; i<maxAssociation; i++) fontNames[i] = null;
for(int i=0; i<associationArray.length; i++)
{
if (associationArray[i] == null) continue;
int fontNumber = TextUtils.atoi(associationArray[i]);
if (fontNumber <= 0) continue;
int slashPos = associationArray[i].indexOf('/');
if (slashPos < 0) continue;
fontNames[fontNumber-1] = associationArray[i].substring(slashPos+1);
}
}
/**
* Method to convert the font number in a TextDescriptor to the proper value as
* cached in the Library. The caching is examined by "getFontAssociationVariable()".
* @param descriptor1 value of descriptor1 from disk.
* @return patched value of descriptor1.
*/
private int fixTextDescriptorFont(int descriptor1)
{
int fontNumber = (descriptor1 & ELIBConstants.VTFACE) >> ELIBConstants.VTFACESH;
if (fontNumber == 0) return descriptor1;
descriptor1 &= ~ELIBConstants.VTFACE;
if (fontNames != null && fontNumber <= fontNames.length)
{
String fontName = fontNames[fontNumber-1];
TextDescriptor.ActiveFont af = TextDescriptor.ActiveFont.findActiveFont(fontName);
if (af != null) {
fontNumber = af.getIndex();
if (fontNumber <= (ELIBConstants.VTFACE >> ELIBConstants.VTFACESH))
descriptor1 |= fontNumber << ELIBConstants.VTFACESH;
}
}
return descriptor1;
}
/**
* Set line number for following errors and warnings.
* @param lineNumber line numnber for following erros and warnings.
*/
void setLineNumber(int lineNumber) {}
/**
* Issue error message.
* @param message message string.
* @return MessageLog object for attaching further geometry details.
*/
void logError(String message)
{
errorCount++;
System.out.println(message);
errorLogger.logError(message, -1);
}
/**
* Issue warning message.
* @param message message string.
* @return MessageLog object for attaching further geometry details.
*/
void logWarning(String message)
{
System.out.println(message);
errorLogger.logWarning(message, null, -1);
}
// *************************** THE CELL CLEANUP INTERFACE ***************************
protected void computeTech(Cell cell, Set uncomputedCells)
{
uncomputedCells.remove(cell);
}
protected double computeLambda(Cell cell, int cellIndex) { return 1; }
protected boolean spreadLambda(Cell cell, int cellIndex) { return false; }
protected boolean canScale() { return false; }
/**
* Method to recursively create the contents of each cell in the library.
*/
abstract void realizeCellsRecursively(Cell cell, HashSet<Cell> recursiveSetupFlag, HashSet<Cell> patchedCells, String scaledCellName, double scale);
}