/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: JelibParser.java
* Input/output tool: JELIB Library input
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004, 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.ImmutableArcInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.id.ArcProtoId;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.id.ExportId;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.id.LibId;
import com.sun.electric.database.id.NodeProtoId;
import com.sun.electric.database.id.PortProtoId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.PrimitivePortId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.text.CellName;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.CodeExpression;
import com.sun.electric.database.variable.MutableTextDescriptor;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.ncc.basic.TransitiveRelation;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.util.math.Orientation;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
/**
* This class reads files in new library file (.jelib) format.
*/
public class JelibParser
{
class CellContents
{
final Version version;
boolean filledIn;
String fileName;
int lineNumber;
CellId cellId;
CellName groupName;
long creationDate;
long revisionDate;
TechId techId;
boolean expanded;
boolean allLocked;
boolean instLocked;
boolean cellLib;
boolean techLib;
Variable[] vars;
List<NodeContents> nodes = new ArrayList<NodeContents>();
List<ExportContents> exports = new ArrayList<ExportContents>();
List<ArcContents> arcs = new ArrayList<ArcContents>();
// map disk node names (duplicate node names written "sig"1 and "sig"2)
HashMap<String,NodeContents> diskName = new HashMap<String,NodeContents>();
CellContents(Version version)
{
this.version = version;
filledIn = false;
}
}
static class NodeContents {
int line;
NodeProtoId protoId;
String nodeName;
TextDescriptor nameTextDescriptor;
EPoint anchor;
Orientation orient;
EPoint size;
TextDescriptor protoTextDescriptor;
int flags;
int techBits;
Variable[] vars;
ImmutableNodeInst n;
NodeInst ni;
}
static class ExportContents {
int line;
ExportId exportId;
String exportUserName;
NodeContents originalNode;
PortProtoId originalPort;
TextDescriptor nameTextDescriptor;
PortCharacteristic ch;
boolean alwaysDrawn;
boolean bodyOnly;
Variable[] vars;
Point2D pos;
}
static class ArcContents {
int line;
ArcProtoId arcProtoId;
String arcName;
TextDescriptor nameTextDescriptor;
double diskWidth;
NodeContents headNode;
PortProtoId headPort;
EPoint headPoint;
NodeContents tailNode;
PortProtoId tailPort;
EPoint tailPoint;
int angle;
int flags;
Variable[] vars;
}
// The parsing result
Version version;
Variable[] libVars;
public final LinkedHashMap<LibId,String> externalLibIds = new LinkedHashMap<LibId,String>();
final LinkedHashMap<CellId,Rectangle2D> externalCells = new LinkedHashMap<CellId,Rectangle2D>();
final LinkedHashMap<ExportId,EPoint> externalExports = new LinkedHashMap<ExportId,EPoint>();
final LinkedHashMap<TechId,Variable[]> techIds = new LinkedHashMap<TechId,Variable[]>();
final LinkedHashMap<PrimitiveNodeId,Variable[]> primitiveNodeIds = new LinkedHashMap<PrimitiveNodeId,Variable[]>();
final LinkedHashMap<PrimitivePortId,Variable[]> primitivePortIds = new LinkedHashMap<PrimitivePortId,Variable[]>();
final LinkedHashMap<ArcProtoId,Variable[]> arcProtoIds = new LinkedHashMap<ArcProtoId,Variable[]>();
final LinkedHashMap<String,Variable[]> tools = new LinkedHashMap<String,Variable[]>();
final LinkedHashMap<CellId,CellContents> allCells = new LinkedHashMap<CellId,CellContents>();
// final LinkedHashSet<String> delibCellFiles = new LinkedHashSet<String>();
/*---------------------------------------------------------------------*/
private static String[] revisions =
{
// Revision 1
"8.01aw",
// Revision 2
"8.04l",
};
private static final Version newDelibHeaderVersion = Version.parseVersion("8.04n");
private static int defaultArcFlags;
static {
defaultArcFlags = ImmutableArcInst.DEFAULT_FLAGS;
defaultArcFlags = ImmutableArcInst.HARD_SELECT.set(defaultArcFlags, false); // A
defaultArcFlags = ImmutableArcInst.BODY_ARROWED.set(defaultArcFlags, false); // B
defaultArcFlags = ImmutableArcInst.FIXED_ANGLE.set(defaultArcFlags, true); // F
defaultArcFlags = ImmutableArcInst.HEAD_NEGATED.set(defaultArcFlags, false); // G
defaultArcFlags = ImmutableArcInst.HEAD_EXTENDED.set(defaultArcFlags, true); // I
defaultArcFlags = ImmutableArcInst.TAIL_EXTENDED.set(defaultArcFlags, true); // J
defaultArcFlags = ImmutableArcInst.TAIL_NEGATED.set(defaultArcFlags, false); // N
defaultArcFlags = ImmutableArcInst.RIGID.set(defaultArcFlags, false); // R
defaultArcFlags = ImmutableArcInst.SLIDABLE.set(defaultArcFlags, false); // S
defaultArcFlags = ImmutableArcInst.HEAD_ARROWED.set(defaultArcFlags, false); // X
defaultArcFlags = ImmutableArcInst.TAIL_ARROWED.set(defaultArcFlags, false); // Y
}
// Were in LibraryFiles
final URL fileURL;
private final FileType fileType;
private final IdManager idManager;
private final LibId libId;
private final String filePath;
private LineNumberReader lineReader;
private final LineNumberReader delibHeaderReader;
private int revision = revisions.length;
private final ErrorLogger errorLogger;
private final MutableTextDescriptor mtd = new MutableTextDescriptor();
/** buffer for reading Variables. */ private final ArrayList<Variable> variablesBuf = new ArrayList<Variable>();
// collect the cells by common protoName and by "groupLines" relation
private final HashMap<String,ArrayList<CellContents>> cellsWithProtoName = new HashMap<String,ArrayList<CellContents>>();
private final TransitiveRelation<String> transitiveProtoNames = new TransitiveRelation<String>();
private HashMap<String,TextDescriptorAndCode> parsedDescriptorsF = new HashMap<String,TextDescriptorAndCode>();
private HashMap<String,TextDescriptorAndCode> parsedDescriptorsT = new HashMap<String,TextDescriptorAndCode>();
// private Version version;
private char escapeChar = '\\';
private String curLibName;
private String curReadFile;
private static class TextDescriptorAndCode {
private final TextDescriptor td;
private final CodeExpression.Code code;
TextDescriptorAndCode(TextDescriptor td, CodeExpression.Code code) {
this.td = td;
this.code = code;
}
}
private LibId curExternalLibId = null;
private CellId curExternalCellId = null;
private TechId curTechId = null;
private PrimitiveNodeId curPrimId = null;
private JelibParser(LibId libId, URL fileURL, FileType fileType, boolean onlyProjectSettings, ErrorLogger errorLogger) throws IOException
{
idManager = libId.idManager;
this.libId = libId;
this.fileURL = fileURL;
this.fileType = fileType;
filePath = fileURL.getFile();
this.errorLogger = errorLogger;
InputStream inputStream;
if (fileType == FileType.JELIB) {
URLConnection urlCon = fileURL.openConnection();
urlCon.setConnectTimeout(10000);
urlCon.setReadTimeout(1000);
curReadFile = filePath;
inputStream = urlCon.getInputStream();
} else if (fileType == FileType.DELIB) {
curReadFile = filePath + File.separator + "header";
try{
inputStream = new FileInputStream(curReadFile);
} catch (IOException e)
{
String message = "Header file " + curReadFile + " not found";
System.out.println(message);
throw new FileNotFoundException(message);
}
} else {
throw new IllegalArgumentException("fileType");
}
InputStreamReader is = new InputStreamReader(inputStream, "UTF-8");
this.lineReader = new LineNumberReader(is);
delibHeaderReader = fileType == FileType.DELIB ? lineReader : null;
try {
readFromFile(onlyProjectSettings);
collectCellGroups();
} catch (Exception e) {
logError("Exception " + e.getMessage());
} finally {
lineReader.close();
}
}
private void collectCellGroups() {
for (Iterator<Set<String>> git = transitiveProtoNames.getSetsOfRelatives(); git.hasNext(); ) {
Set<String> protoNames = git.next();
// Collect cells in this group
ArrayList<CellContents> cellsInGroup = new ArrayList<CellContents>();
for (String protoName: protoNames) {
ArrayList<CellContents> list = cellsWithProtoName.get(protoName);
if (list == null) {
logError("No cells for group name " + protoName);
continue;
}
cellsInGroup.addAll(list);
}
// Make cell group name
ArrayList<CellName> cellNamesInGroup = new ArrayList<CellName>();
for (CellContents cc: cellsInGroup)
cellNamesInGroup.add(cc.cellId.cellName);
CellName groupName = Snapshot.makeCellGroupName(cellNamesInGroup);
// Set cell group name for each cell
for (CellContents cc: cellsInGroup)
cc.groupName = groupName;
}
}
public static JelibParser parse(LibId libId, URL fileURL, FileType fileType, boolean onlyProjectSettings, ErrorLogger errorLogger) throws IOException {
return new JelibParser(libId, fileURL, fileType, onlyProjectSettings, errorLogger);
}
private void readFromFile(boolean onlyProjectSettings) throws IOException {
boolean ignoreCvsMergedContent = false;
for(;;)
{
// get keyword from file
String line = lineReader.readLine();
if (line == null) break;
// ignore blanks and comments
if (line.length() == 0) continue;
char first = line.charAt(0);
if (first == '#') continue;
if (line.startsWith("<<<<<<<")) {
// This marks start of stuff merged from CVS, ignore this stuff
ignoreCvsMergedContent = true;
logError("CVS conflicts found: " + line);
continue;
}
if (ignoreCvsMergedContent && line.startsWith("=======")) {
// This marks start of local stuff merged, use this stuff
ignoreCvsMergedContent = false;
continue;
}
if (line.startsWith(">>>>>>>")) {
// This marks end of cvs merging
continue;
}
if (ignoreCvsMergedContent) continue;
if (onlyProjectSettings && first != 'H' && first != 'O' && first != 'T') continue;
if (first == 'C')
{
if (lineReader == delibHeaderReader) {
readDelibCell(line);
} else {
readCell(line);
}
continue;
}
if (first == 'L')
{
// cross-library reference
List<String> pieces = parseLine(line);
if (pieces.size() != 2)
{
logError("External library declaration needs 2 fields: " + line);
continue;
}
String libName = unQuote(pieces.get(0));
curExternalLibId = idManager.newLibId(libName);
String libFileName = unQuote(pieces.get(1));
// recurse
if (!externalLibIds.containsKey(curExternalLibId))
externalLibIds.put(curExternalLibId, libFileName);
continue;
}
if (first == 'R') {
// cross-library cell information
List<String> pieces = parseLine(line);
int numPieces = revision == 1 ? 7 : 5;
if (pieces.size() != numPieces) {
logError("External cell declaration needs " + numPieces + " fields: " + line);
continue;
}
double lowX = readDouble(pieces.get(1));
double highX = readDouble(pieces.get(2));
double lowY = readDouble(pieces.get(3));
double highY = readDouble(pieces.get(4));
if (revision == 1)
{
Long.parseLong(pieces.get(5)); // ignore cdate
Long.parseLong(pieces.get(6)); // ignore rdate
}
Rectangle2D bounds = new Rectangle2D.Double(lowX, lowY, highX-lowX, highY-lowY);
String cellName = unQuote(pieces.get(0));
curExternalCellId = curExternalLibId.newCellId(CellName.parseName(cellName));
if (!externalCells.containsKey(curExternalCellId))
externalCells.put(curExternalCellId, bounds);
continue;
}
if (first == 'F') {
// cross-library export information
List<String> pieces = parseLine(line);
if (pieces.size() != 3) {
logError("External export declaration needs 3 fields: " + line);
continue;
}
String exportName = unQuote(pieces.get(0));
double posX = readDouble(pieces.get(1));
double posY = readDouble(pieces.get(1));
ExportId exportId = curExternalCellId.newPortId(exportName);
externalExports.put(exportId, EPoint.fromLambda(posX, posY));
continue;
}
if (first == 'H') {
// parse header
List<String> pieces = parseLine(line);
if (pieces.size() < 2) {
logError("Library declaration needs 2 fields: " + line);
continue;
}
version = Version.parseVersion(pieces.get(1));
if (version == null) {
logError("Badly formed version: " + pieces.get(1));
continue;
}
for (revision = 0; revision < revisions.length; revision++) {
if (version.compareTo(Version.parseVersion(revisions[revision])) < 0) break;
}
escapeChar = revision < 1 ? '^' : '\\';
pieces = parseLine(line);
curLibName = unQuote(pieces.get(0));
if (version.compareTo(Version.getVersion()) > 0) {
logWarning("Library " + curLibName + " comes from a NEWER version of Electric (" + version + ")");
}
libVars = readVariables(pieces, 2);
continue;
}
if (first == 'O')
{
// parse Tool information
List<String> pieces = parseLine(line);
String toolName = unQuote(pieces.get(0));
// get additional meaning preferences starting at position 1
Variable[] vars = readVariables(pieces, 1);
if (!tools.containsKey(toolName))
tools.put(toolName, vars);
continue;
}
if (first == 'V') {
// parse View information
List<String> pieces = parseLine(line);
String viewName = unQuote(pieces.get(0));
View view = View.findView(viewName);
if (view == null) {
String viewAbbr = unQuote(pieces.get(1));
view = View.newInstance(viewName, viewAbbr);
if (view == null) {
logError("Cannot create view " + viewName);
continue;
}
}
// get additional variables starting at position 2
Variable[] vars = readVariables(pieces, 2);
continue;
}
if (first == 'T') {
// parse Technology information
List<String> pieces = parseLine(line);
String techName = unQuote(pieces.get(0));
curTechId = idManager.newTechId(techName);
curPrimId = null;
// get additional meaning preferences starting at position 1
Variable[] vars = readVariables(pieces, 1);
if (!techIds.containsKey(curTechId))
techIds.put(curTechId, vars);
continue;
}
if (first == 'D') {
// parse PrimitiveNode information
List<String> pieces = parseLine(line);
String primName = unQuote(pieces.get(0));
if (curTechId == null) {
logError("Primitive node " + primName + " has no technology before it");
continue;
}
curPrimId = curTechId.newPrimitiveNodeId(primName);
// get additional variables starting at position 1
Variable[] vars = readVariables(pieces, 1);
if (!primitiveNodeIds.containsKey(curPrimId))
primitiveNodeIds.put(curPrimId, vars);
continue;
}
if (first == 'P') {
// parse PrimitivePort information
List<String> pieces = parseLine(line);
String primPortName = unQuote(pieces.get(0));
if (curPrimId == null) {
logError("Primitive port " + primPortName + " has no primitive node before it");
continue;
}
PrimitivePortId primitivePortId = curPrimId.newPortId(primPortName);
// get additional variables starting at position 1
Variable[] vars = readVariables(pieces, 1);
if (!primitivePortIds.containsKey(primitivePortId))
primitivePortIds.put(primitivePortId, vars);
continue;
}
if (first == 'W') {
// parse ArcProto information
List<String> pieces = parseLine(line);
String arcName = unQuote(pieces.get(0));
if (curTechId == null) {
logError("Primitive arc " + arcName + " has no technology before it");
continue;
}
ArcProtoId arcProtoId = curTechId.newArcProtoId(arcName);
// get additional variables starting at position 1
Variable[] vars = readVariables(pieces, 1);
if (!arcProtoIds.containsKey(arcProtoId))
arcProtoIds.put(arcProtoId, vars);
continue;
}
if (first == 'G')
{
// group information
List<String> pieces = parseLine(line);
String firstProtoName = null;
for(int i=0; i<pieces.size(); i++)
{
String cellNameString = unQuote(pieces.get(i));
if (cellNameString.length() == 0) continue;
int colonPos = cellNameString.indexOf(':');
if (colonPos >= 0) cellNameString = cellNameString.substring(colonPos+1);
CellName cellName = CellName.parseName(cellNameString);
if (cellName == null) {
logError("Bad cell name " + cellNameString);
continue;
}
if (cellsWithProtoName.get(cellName.getName()) == null) {
logError("Unknown cell " + cellName);
continue;
}
String protoName = cellName.getName();
if (firstProtoName == null)
firstProtoName = protoName;
else
transitiveProtoNames.theseAreRelated(firstProtoName, protoName);
}
continue;
}
logError("Unrecognized line: " + line);
}
}
private void readDelibCell(String line) throws IOException {
// get the file location; remove 'C' at start
String cellFile = line.substring(1, line.length());
// New header file as of version 8.04n, no cell refs, searches delib dir for cell files
if (version.compareTo(newDelibHeaderVersion) >= 0) {
if (cellFile.equals(com.sun.electric.tool.io.output.DELIB.SEARCH_FOR_CELL_FILES)) {
File delibDir = new File(filePath);
if (delibDir.exists() && delibDir.isDirectory()) {
for (File file : delibDir.listFiles()) {
if (file.isDirectory()) continue;
String name = file.getName();
int dot = name.lastIndexOf('.');
if (dot < 0) continue;
View view = View.findView(name.substring(dot+1));
if (view == null) continue;
try {
readDelibFile(file);
} catch (Exception e) {
if (e instanceof IOException) throw (IOException)e;
// some other exception, probably invalid cell file
Input.errorLogger.logError("Exception reading file "+file, -1);
}
}
}
}
return;
}
cellFile = cellFile.replace(com.sun.electric.tool.io.output.DELIB.PLATFORM_INDEPENDENT_FILE_SEPARATOR, File.separatorChar);
cellFile = cellFile.replace(File.separatorChar, ':');
File cellFD = new File(filePath, cellFile);
readDelibFile(cellFD);
}
private void readDelibFile(File cellFD) throws IOException {
LineNumberReader cellReader;
try {
FileInputStream fin = new FileInputStream(cellFD);
InputStreamReader is = new InputStreamReader(fin);
cellReader = new LineNumberReader(is);
} catch (IOException e) {
System.out.println("Error opening file "+cellFD+": "+e.getMessage());
return;
}
Version savedVersion = version;
int savedRevision = revision;
char savedEscapeChar = escapeChar;
String savedCurLibName = curLibName;
lineReader = cellReader;
curReadFile = cellFD.getAbsolutePath();
try {
readFromFile(false);
// delibCellFiles.add(curReadFile);
} finally {
version = savedVersion;
revision = savedRevision;
escapeChar = savedEscapeChar;
curLibName = savedCurLibName;
lineReader.close();
lineReader = delibHeaderReader;
curReadFile = filePath;
}
}
private void readCell(String line) throws IOException {
// grab a cell description
List<String> pieces = parseLine(line);
int numPieces = revision >= 2 ? 6 : revision == 1 ? 5 : 7;
if (pieces.size() < numPieces)
{
logError("Cell declaration needs " + numPieces + " fields: " + line);
return;
}
int fieldIndex = 0;
String name;
String groupName = null;
if (revision >= 1) {
name = unQuote(pieces.get(fieldIndex++));
if (revision >= 2) {
String s = pieces.get(fieldIndex++);
if (s.length() > 0)
groupName = unQuote(s);
}
} else {
name = unQuote(pieces.get(fieldIndex++));
String viewAbbrev = pieces.get(fieldIndex++);
String versionString = pieces.get(fieldIndex++);
name = name + ";" + versionString + "{" + viewAbbrev + "}";
}
CellName cellName = CellName.parseName(name);
CellContents cc = new CellContents(version);
cc.fileName = curReadFile;
cc.lineNumber = lineReader.getLineNumber() + 1;
cc.cellId = libId.newCellId(cellName);
String techName = unQuote(pieces.get(fieldIndex++));
cc.techId = idManager.newTechId(techName);
cc.creationDate = Long.parseLong(pieces.get(fieldIndex++));
cc.revisionDate = Long.parseLong(pieces.get(fieldIndex++));
// parse state information
String stateInfo = pieces.get(fieldIndex++);
for(int i=0; i<stateInfo.length(); i++)
{
switch (stateInfo.charAt(i)) {
case 'E': cc.expanded = true; break;
case 'L': cc.allLocked = true; break;
case 'I': cc.instLocked = true; break;
case 'C': cc.cellLib = true; break;
case 'T': cc.techLib = true; break;
}
}
// add variables
assert fieldIndex == numPieces;
cc.vars = readVariables(pieces, numPieces);
// gather the contents of the cell
for(;;)
{
String nextLine = lineReader.readLine();
if (nextLine == null) break;
if (nextLine.length() == 0) continue;
char nextFirst = nextLine.charAt(0);
if (nextFirst == 'X') break;
switch (nextFirst) {
case '#':
break;
case 'N':
case 'I':
parseNode(nextLine, cc);
break;
case 'E':
parseExport(nextLine, cc);
break;
case 'A':
parseArc(nextLine, cc);
break;
default:
}
}
// check if the version is not null
if (cc.version == null)
{
logError("Version for Cell '" + cc.cellId.cellName + "' is null");
return;
}
// remember the contents of the cell for later
if (allCells.containsKey(cc.cellId)) {
logError("Duplicate cell " + cc.cellId);
return;
}
String protoName = cellName.getName();
if (groupName == null)
groupName = protoName;
transitiveProtoNames.theseAreRelated(protoName, groupName);
allCells.put(cc.cellId, cc);
ArrayList<CellContents> list = cellsWithProtoName.get(protoName);
if (list == null) {
list = new ArrayList<CellContents>();
cellsWithProtoName.put(protoName, list);
}
list.add(cc);
return;
}
private void parseNode(String cellString, CellContents cc) {
NodeContents n = new NodeContents();
n.line = lineReader.getLineNumber();
// parse the node line
List<String> pieces = parseLine(cellString);
char firstChar = cellString.charAt(0);
int numPieces = revision < 1 ? 10 : firstChar == 'N' ? 9 : 8;
if (pieces.size() < numPieces)
{
logError("Node instance needs " + numPieces + " fields: " + cellString, cc.cellId);
return;
}
String protoName = unQuote(pieces.get(0));
// figure out the name for this node. Handle the form: "Sig"12
String diskNodeName = revision >= 1 ? pieces.get(1) : unQuote(pieces.get(1));
String nodeName = diskNodeName;
if (nodeName.charAt(0) == '"')
{
int lastQuote = nodeName.lastIndexOf('"');
if (lastQuote > 1)
{
nodeName = nodeName.substring(1, lastQuote);
if (revision >= 1) nodeName = unQuote(nodeName);
}
}
n.nodeName = nodeName;
String nameTextDescriptorInfo = pieces.get(2);
double x = readDouble(pieces.get(3));
double y = readDouble(pieces.get(4));
LibId libId = cc.cellId.libId;
String prefixName = libId.libName;
int colonPos = protoName.indexOf(':');
if (colonPos < 0)
{
if (firstChar == 'I' || revision < 1)
n.protoId = libId.newCellId(CellName.parseName(protoName));
else
n.protoId = cc.techId.newPrimitiveNodeId(protoName);
} else
{
prefixName = protoName.substring(0, colonPos);
protoName = protoName.substring(colonPos+1);
if (firstChar == 'I' || revision < 1 && protoName.indexOf('{') >= 0) {
if (!prefixName.equals(curLibName))
libId = idManager.newLibId(prefixName);
n.protoId = libId.newCellId(CellName.parseName(protoName));
} else {
n.protoId = idManager.newTechId(prefixName).newPrimitiveNodeId(protoName);
}
}
n.size = EPoint.ORIGIN;
boolean flipX = false, flipY = false;
String orientString;
String stateInfo;
String textDescriptorInfo = "";
if (firstChar == 'N' || revision < 1)
{
double wid = readDouble(pieces.get(5));
if (revision < 1 && (wid < 0 || wid == 0 && 1/wid < 0)) {
flipX = true;
wid = -wid;
}
double hei = readDouble(pieces.get(6));
if (revision < 1 && (hei < 0 || hei == 0 && 1/hei < 0)) {
flipY = true;
hei = -hei;
}
if (n.protoId instanceof PrimitiveNodeId)
n.size = EPoint.fromLambda(wid, hei);
orientString = pieces.get(7);
stateInfo = pieces.get(8);
if (revision < 1)
textDescriptorInfo = pieces.get(9);
} else
{
orientString = pieces.get(5);
stateInfo = pieces.get(6);
textDescriptorInfo = pieces.get(7);
}
int angle = 0;
for (int i = 0; i < orientString.length(); i++)
{
char ch = orientString.charAt(i);
if (ch == 'X') flipX = !flipX;
else if (ch == 'Y') flipY = !flipY;
else if (ch == 'R') angle += 900;
else
{
angle += Integer.valueOf(orientString.substring(i));
break;
}
}
// parse state information in stateInfo field
TextDescriptorAndCode nameTdC = loadTextDescriptor(nameTextDescriptorInfo, false);
n.nameTextDescriptor = nameTdC.td;
int flags = 0, techBits = 0;
// parse state information in jelibUserBits
parseStateInfo:
for(int i=0; i<stateInfo.length(); i++) {
char chr = stateInfo.charAt(i);
switch (chr) {
case 'E': /*flags = ImmutableNodeInst.EXPAND.set(flags, true);*/ break;
case 'L': flags = ImmutableNodeInst.LOCKED.set(flags, true); break;
case 'S': /*userBits |= NSHORT;*/ break; // deprecated
case 'V': flags = ImmutableNodeInst.VIS_INSIDE.set(flags, true); break;
case 'W': /*flags = ImmutableNodeInst.WIPED.set(flags, true);*/ break; // deprecated
case 'A': flags = ImmutableNodeInst.HARD_SELECT.set(flags, true); break;
default:
if (Character.isDigit(chr)) {
stateInfo = stateInfo.substring(i);
try {
techBits = Integer.parseInt(stateInfo);
} catch (NumberFormatException e) {
logError("(" + cc.cellId + ") bad node bits" + stateInfo, cc.cellId);
}
break parseStateInfo;
}
}
}
n.flags = flags;
n.techBits = techBits;
TextDescriptorAndCode protoTdC = loadTextDescriptor(textDescriptorInfo, false);
n.protoTextDescriptor = protoTdC.td;
// create the node
n.orient = Orientation.fromJava(angle, flipX, flipY);
n.anchor = EPoint.fromLambda(x, y);
// add variables in fields 10 and up
n.vars = readVariables(pieces, numPieces);
cc.nodes.add(n);
// insert into map of disk names
cc.diskName.put(diskNodeName, n);
}
private void parseExport(String cellString, CellContents cc) {
ExportContents e = new ExportContents();
e.line = lineReader.getLineNumber();
// parse the export line
List<String> pieces = parseLine(cellString);
if (revision >= 2 && pieces.size() == 1) {
// Unused ExportId
String exportName = unQuote(pieces.get(0));
cc.cellId.newPortId(exportName);
return;
}
int numPieces = revision >= 2 ? 6 : revision == 1 ? 5 : 7;
if (pieces.size() < numPieces)
{
logError("Export needs " + numPieces + " fields, has " + pieces.size() + ": " + cellString, cc.cellId);
return;
}
int fieldIndex = 0;
String exportName = unQuote(pieces.get(fieldIndex++));
String exportUserName = null;
if (revision >= 2) {
String s = pieces.get(fieldIndex++);
if (s.length() != 0)
exportUserName = unQuote(s);
}
if (exportUserName == null || exportName.equals(exportUserName))
exportName = Name.findName(exportName).toString(); // save memory using String from Name
e.exportId = cc.cellId.newPortId(exportName);
e.exportUserName = exportUserName;
// get text descriptor in field 1
String textDescriptorInfo = pieces.get(fieldIndex++);
String nodeName = revision >= 1 ? pieces.get(fieldIndex++) : unQuote(pieces.get(fieldIndex++));
e.originalNode = cc.diskName.get(nodeName);
String portName = unQuote(pieces.get(fieldIndex++));
e.originalPort = e.originalNode.protoId.newPortId(portName);
Point2D pos = null;
if (revision < 1)
{
double x = readDouble(pieces.get(fieldIndex++));
double y = readDouble(pieces.get(fieldIndex++));
pos = new Point2D.Double(x, y);
}
e.pos = pos;
// parse state information in field 6
String userBits = pieces.get(fieldIndex++);
assert fieldIndex == numPieces;
TextDescriptorAndCode nameTdC = loadTextDescriptor(textDescriptorInfo, false);
e.nameTextDescriptor = nameTdC.td;
// parse state information
int slashPos = userBits.indexOf('/');
if (slashPos >= 0) {
String extras = userBits.substring(slashPos);
userBits = userBits.substring(0, slashPos);
while (extras.length() > 0) {
switch (extras.charAt(1)) {
case 'A': e.alwaysDrawn = true; break;
case 'B': e.bodyOnly = true; break;
}
extras = extras.substring(2);
}
}
PortCharacteristic ch = PortCharacteristic.findCharacteristicShort(userBits);
e.ch = ch != null ? ch : PortCharacteristic.UNKNOWN;
// add variables in tail fields
e.vars = readVariables(pieces, numPieces);
cc.exports.add(e);
}
private void parseArc(String cellString, CellContents cc) {
ArcContents a = new ArcContents();
a.line = lineReader.getLineNumber();
// parse the arc line
List<String> pieces = parseLine(cellString);
if (pieces.size() < 13)
{
logError("Arc instance needs 13 fields: " + cellString, cc.cellId);
return;
}
TechId techId = cc.techId;
String protoName = unQuote(pieces.get(0));
int indexOfColon = protoName.indexOf(':');
if (indexOfColon >= 0) {
techId = idManager.newTechId(protoName.substring(0, indexOfColon));
protoName = protoName.substring(indexOfColon + 1);
}
a.arcProtoId = techId.newArcProtoId(protoName);
String diskArcName = revision >= 1 ? pieces.get(1) : unQuote(pieces.get(1));
String arcName = diskArcName;
if (arcName.charAt(0) == '"')
{
int lastQuote = arcName.lastIndexOf('"');
if (lastQuote > 1)
{
arcName = arcName.substring(1, lastQuote);
if (revision >= 1) arcName = unQuote(arcName);
}
}
a.arcName = arcName;
a.diskWidth = readDouble(pieces.get(3));
String headNodeName = revision >= 1 ? pieces.get(5) : unQuote(pieces.get(5));
String headPortName = unQuote(pieces.get(6));
double headX = readDouble(pieces.get(7));
double headY = readDouble(pieces.get(8));
a.headNode = cc.diskName.get(headNodeName);
a.headPort = a.headNode.protoId.newPortId(headPortName);
a.headPoint = EPoint.fromLambda(headX, headY);
String tailNodeName = revision >= 1 ? pieces.get(9) : unQuote(pieces.get(9));
String tailPortName = unQuote(pieces.get(10));
double tailX = readDouble(pieces.get(11));
double tailY = readDouble(pieces.get(12));
a.tailNode = cc.diskName.get(tailNodeName);
a.tailPort = a.tailNode.protoId.newPortId(tailPortName);
a.tailPoint = EPoint.fromLambda(tailX, tailY);
// parse state information in field 4
String stateInfo = pieces.get(4);
boolean extended = true, directional = false, reverseEnds = false,
skipHead = false, skipTail = false,
tailNotExtended = false, headNotExtended = false,
tailArrowed = false, headArrowed = false, bodyArrowed = false;
// Default flags are false except FIXED_ANGLE
// SLIDABLE
int flags = defaultArcFlags;
int angle = ArcInst.DEFAULTANGLE;
parseStateInfo:
for(int i=0; i<stateInfo.length(); i++)
{
char chr = stateInfo.charAt(i);
switch (chr) {
case 'R': flags = ImmutableArcInst.RIGID.set(flags, true); break;
case 'F': flags = ImmutableArcInst.FIXED_ANGLE.set(flags, false); break;
case 'S': flags = ImmutableArcInst.SLIDABLE.set(flags, true); break;
case 'A': flags = ImmutableArcInst.HARD_SELECT.set(flags, true); break;
case 'N': flags = ImmutableArcInst.TAIL_NEGATED.set(flags, true); break;
case 'G': flags = ImmutableArcInst.HEAD_NEGATED.set(flags, true); break;
case 'X': headArrowed = true; break;
case 'Y': tailArrowed = true; break;
case 'B': bodyArrowed = true; break;
case 'I': headNotExtended = true; break;
case 'J': tailNotExtended = true; break;
// deprecated
case 'E': extended = false; break;
case 'D': directional = true; break;
case 'V': reverseEnds = true; break;
case 'H': skipHead = true; break;
case 'T': skipTail = true; break;
default:
if ('0' <= chr && chr <= '9')
{
angle = Integer.valueOf(stateInfo.substring(i));
break parseStateInfo;
}
}
}
// if old bits were used, convert them
if (!extended || directional)
{
if (!extended) headNotExtended = tailNotExtended = true;
if (directional)
{
if (reverseEnds) tailArrowed = true; else
headArrowed = true;
bodyArrowed = true;
}
if (skipHead) headArrowed = headNotExtended = false;
if (skipTail) tailArrowed = tailNotExtended = false;
}
// set the bits
flags = ImmutableArcInst.HEAD_EXTENDED.set(flags, !headNotExtended);
flags = ImmutableArcInst.TAIL_EXTENDED.set(flags, !tailNotExtended);
flags = ImmutableArcInst.HEAD_ARROWED.set(flags, headArrowed);
flags = ImmutableArcInst.TAIL_ARROWED.set(flags, tailArrowed);
flags = ImmutableArcInst.BODY_ARROWED.set(flags, bodyArrowed);
a.angle = angle;
a.flags = flags;
// get the ard name text descriptor
String nameTextDescriptorInfo = pieces.get(2);
TextDescriptorAndCode nameTdC = loadTextDescriptor(nameTextDescriptorInfo, false);
a.nameTextDescriptor = nameTdC.td;
// add variables in fields 13 and up
a.vars = readVariables(pieces, 13);
cc.arcs.add(a);
}
/**
* Method to parse a line from the file, breaking it into a List of Strings.
* Each field in the file is separated by "|".
* Quoted strings are handled properly, as are the escape character.
* @param line the text from the file.
* @return a List of Strings.
*/
private List<String> parseLine(String line)
{
List<String> stringPieces = new ArrayList<String>();
int len = line.length();
int pos = 1;
int startPos = 1;
boolean inQuote = false;
while (pos < len)
{
char chr = line.charAt(pos++);
if (chr == escapeChar)
{
pos++;
continue;
}
if (chr == '"') inQuote = !inQuote;
if (chr == '|' && !inQuote)
{
stringPieces.add(line.substring(startPos, pos - 1));
startPos = pos;
}
}
if (pos > len) pos = len;
stringPieces.add(line.substring(startPos, pos));
return stringPieces;
}
private String unQuote(String line)
{
int len = line.length();
if (revision >= 1)
{
if (len < 2 || line.charAt(0) != '"') return line;
int lastQuote = line.lastIndexOf('"');
if (lastQuote != len - 1)
return unQuote(line.substring(0, lastQuote + 1)) + line.substring(lastQuote + 1);
line = line.substring(1, len - 1);
len -= 2;
} else
{
if (line.indexOf(escapeChar) < 0) return line;
}
StringBuffer sb = new StringBuffer();
assert len == line.length();
for(int i=0; i<len; i++)
{
char chr = line.charAt(i);
if (chr == escapeChar)
{
i++;
if (i >= len) break;
chr = line.charAt(i);
if (chr == 'n' && revision >= 1) chr = '\n';
if (chr == 'r' && revision >= 1) chr = '\r';
}
sb.append(chr);
}
return sb.toString();
}
/**
* Method to read variables to an ElectricObject from a List of strings.
* @param pieces the array of Strings that described the ElectricObject.
* @param position the index in the array of strings where Variable descriptions begin.
* @return an array of Variables.
*/
private Variable[] readVariables(List<String> pieces, int position)
{
variablesBuf.clear();
int total = pieces.size();
for(int i=position; i<total; i++)
{
String piece = pieces.get(i);
int openPos = 0;
boolean inQuote = false;
for(; openPos < piece.length(); openPos++)
{
char chr = piece.charAt(openPos);
if (chr == escapeChar) { openPos++; continue; }
if (chr == '"') inQuote = !inQuote;
if (chr == '(' && !inQuote) break;
}
if (openPos >= piece.length())
{
logError("Badly formed variable (no open parenthesis): " + piece);
continue;
}
String varName = unQuote(piece.substring(0, openPos));
Variable.Key varKey = Variable.newKey(varName);
int closePos = piece.indexOf(')', openPos);
if (closePos < 0)
{
logError("Badly formed variable (no close parenthesis): " + piece);
continue;
}
String varBits = piece.substring(openPos+1, closePos);
int objectPos = closePos + 1;
if (objectPos >= piece.length())
{
logError("Variable type missing: " + piece);
continue;
}
char varType = piece.charAt(objectPos++);
switch (varType)
{
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'L':
case 'O':
case 'P':
case 'R':
case 'S':
case 'T':
case 'V':
case 'Y':
break; // break from switch
default:
logError("Variable type invalid: " + piece);
continue; // continue loop
}
Object obj = null;
if (objectPos >= piece.length())
{
continue;
} else
{
if (piece.charAt(objectPos) == '[')
{
List<Object> objList = new ArrayList<Object>();
objectPos++;
while (objectPos < piece.length())
{
int start = objectPos;
inQuote = false;
while (objectPos < piece.length())
{
if (inQuote)
{
if (piece.charAt(objectPos) == escapeChar)
{
objectPos++;
} else if (piece.charAt(objectPos) == '"')
{
inQuote = false;
}
objectPos++;
continue;
}
if (piece.charAt(objectPos) == ',' || piece.charAt(objectPos) == ']') break;
if (piece.charAt(objectPos) == '"')
{
inQuote = true;
}
objectPos++;
}
Object oneObj = getVariableValue(piece.substring(start, objectPos), varType);
objList.add(oneObj);
if (piece.charAt(objectPos) == ']') break;
objectPos++;
}
if (objectPos >= piece.length())
{
logError("Badly formed array (no closed bracket): " + piece);
continue;
}
else if (objectPos < piece.length() - 1)
{
logError("Badly formed array (extra characters after closed bracket): " + piece);
continue;
}
int limit = objList.size();
Object [] objArray;
switch (varType)
{
case 'B': objArray = new Boolean[limit]; break;
case 'C': objArray = new CellId[limit]; break;
case 'D': objArray = new Double[limit]; break;
case 'E': objArray = new ExportId[limit]; break;
case 'F': objArray = new Float[limit]; break;
case 'G': objArray = new Long[limit]; break;
case 'H': objArray = new Short[limit]; break;
case 'I': objArray = new Integer[limit]; break;
case 'L': objArray = new LibId[limit]; break;
case 'O': objArray = new Tool[limit]; break;
case 'P': objArray = new PrimitiveNodeId[limit];break;
case 'R': objArray = new ArcProtoId[limit]; break;
case 'S': objArray = new String[limit]; break;
case 'T': objArray = new TechId[limit]; break;
case 'V': objArray = new EPoint[limit]; break;
case 'Y': objArray = new Byte[limit]; break;
default: throw new AssertionError();
}
for(int j=0; j<limit; j++)
objArray[j] = objList.get(j);
obj = objArray;
} else
{
// a scalar Variable
obj = getVariableValue(piece.substring(objectPos), varType);
if (obj == null) {
// ????
continue;
}
}
}
// create the variable
TextDescriptorAndCode tdc = loadTextDescriptor(varBits, true);
obj = Variable.withCode(obj, tdc.code);
Variable d = Variable.newInstance(varKey, obj, tdc.td);
variablesBuf.add(d);
}
return variablesBuf.toArray(Variable.NULL_ARRAY);
}
/**
* Method to load a TextDescriptor from a String description of it.
* @param varBits the String that describes the TextDescriptor.
* @param onVar true if this TextDescriptor resides on a Variable
* It may be false if the TextDescriptor is on a NodeInst or Export.
* @return loaded TextDescriptor
*/
private TextDescriptorAndCode loadTextDescriptor(String varBits, boolean onVar)
{
HashMap<String,TextDescriptorAndCode> parsedDescriptors = onVar ? parsedDescriptorsT : parsedDescriptorsF;
TextDescriptorAndCode tdc = parsedDescriptors.get(varBits);
if (tdc != null) return tdc;
boolean error = false;
mtd.setCBits(0, 0, 0);
CodeExpression.Code code = CodeExpression.Code.NONE;
if (!onVar) mtd.setDisplay(TextDescriptor.Display.SHOWN);
double xoff = 0, yoff = 0;
for(int j=0; j<varBits.length(); j++)
{
char varBit = varBits.charAt(j);
switch (varBit)
{
case 'D': // display position
case 'd': // display position
mtd.setDisplay(varBit == 'D' ? TextDescriptor.Display.SHOWN : TextDescriptor.Display.HIDDEN);
j++;
if (j >= varBits.length())
{
logError("Incorrect display specification: " + varBits);
error = true;
break;
}
switch (varBits.charAt(j))
{
case '5': mtd.setPos(TextDescriptor.Position.CENT); break;
case '8': mtd.setPos(TextDescriptor.Position.UP); break;
case '2': mtd.setPos(TextDescriptor.Position.DOWN); break;
case '4': mtd.setPos(TextDescriptor.Position.LEFT); break;
case '6': mtd.setPos(TextDescriptor.Position.RIGHT); break;
case '7': mtd.setPos(TextDescriptor.Position.UPLEFT); break;
case '9': mtd.setPos(TextDescriptor.Position.UPRIGHT); break;
case '1': mtd.setPos(TextDescriptor.Position.DOWNLEFT); break;
case '3': mtd.setPos(TextDescriptor.Position.DOWNRIGHT); break;
case '0': mtd.setPos(TextDescriptor.Position.BOXED); break;
}
break;
case 'N': // display type
mtd.setDispPart(TextDescriptor.DispPos.NAMEVALUE);
break;
case 'A': // absolute text size
int semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad absolute size (semicolon missing): " + varBits);
error = true;
break;
}
mtd.setAbsSize(Integer.valueOf(varBits.substring(j+1, semiPos)));
j = semiPos;
break;
case 'G': // relative text size
semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad relative size (semicolon missing): " + varBits);
error = true;
break;
}
mtd.setRelSize(readDouble(varBits.substring(j+1, semiPos)));
j = semiPos;
break;
case 'X': // X offset
semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad X offset (semicolon missing): " + varBits);
error = true;
break;
}
xoff = readDouble(varBits.substring(j+1, semiPos));
j = semiPos;
break;
case 'Y': // Y offset
semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad Y offset (semicolon missing): " + varBits);
error = true;
break;
}
yoff = readDouble(varBits.substring(j+1, semiPos));
j = semiPos;
break;
case 'B': // bold
mtd.setBold(true);
break;
case 'I': // italic
mtd.setItalic(true);
break;
case 'L': // underlined
mtd.setUnderline(true);
break;
case 'F': // font
semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad font (semicolon missing): " + varBits);
error = true;
break;
}
TextDescriptor.ActiveFont af = TextDescriptor.ActiveFont.findActiveFont(varBits.substring(j+1, semiPos));
if (af != null)
mtd.setFace(af.getIndex());
j = semiPos;
break;
case 'C': // color
semiPos = varBits.indexOf(';', j);
if (semiPos < 0)
{
logError("Bad color (semicolon missing): " + varBits);
error = true;
break;
}
mtd.setColorIndex(Integer.valueOf(varBits.substring(j+1, semiPos)));
j = semiPos;
break;
case 'R': // rotation
TextDescriptor.Rotation rot = TextDescriptor.Rotation.ROT90;
if (j+1 < varBits.length() && varBits.charAt(j+1) == 'R')
{
rot = TextDescriptor.Rotation.ROT180;
j++;
}
if (j+1 < varBits.length() && varBits.charAt(j+1) == 'R')
{
rot = TextDescriptor.Rotation.ROT270;
j++;
}
mtd.setRotation(rot);
break;
case 'H': // inheritable
mtd.setInherit(true);
break;
case 'T': // interior
mtd.setInterior(true);
break;
case 'P': // parameter
mtd.setParam(true);
break;
case 'O': // code
j++;
if (j >= varBits.length())
{
logError("Bad language specification: " + varBits);
error = true;
break;
}
char codeLetter = varBits.charAt(j);
if (!onVar)
{
logError("Illegal use of language specification: " + varBits);
error = true;
break;
}
switch (codeLetter) {
case 'J': code = CodeExpression.Code.JAVA; break;
case 'L': code = CodeExpression.Code.SPICE; break;
case 'T': code = CodeExpression.Code.TCL; break;
default:
logError("Unknown language specification: " + varBits);
error = true;
}
break;
case 'U': // units
j++;
if (j >= varBits.length())
{
logError("Bad units specification: " + varBits);
error = true;
break;
}
char unitsLetter = varBits.charAt(j);
if (unitsLetter == 'R') mtd.setUnit(TextDescriptor.Unit.RESISTANCE); else
if (unitsLetter == 'C') mtd.setUnit(TextDescriptor.Unit.CAPACITANCE); else
if (unitsLetter == 'I') mtd.setUnit(TextDescriptor.Unit.INDUCTANCE); else
if (unitsLetter == 'A') mtd.setUnit(TextDescriptor.Unit.CURRENT); else
if (unitsLetter == 'V') mtd.setUnit(TextDescriptor.Unit.VOLTAGE); else
if (unitsLetter == 'D') mtd.setUnit(TextDescriptor.Unit.DISTANCE); else
if (unitsLetter == 'T') mtd.setUnit(TextDescriptor.Unit.TIME); else
{
logError("Unknown units specification: " + varBits);
error = true;
}
break;
}
}
mtd.setOff(xoff, yoff);
TextDescriptor td = TextDescriptor.newTextDescriptor(mtd);
tdc = new TextDescriptorAndCode(td, code);
if (!error) parsedDescriptors.put(varBits, tdc);
return tdc;
}
/**
* Method to convert a String to an Object so that it can be stored in a Variable.
* @param piece the String to be converted.
* @param varType the type of the object to convert (a letter from the file).
* @return the Object representation of the given String.
*/
private Object getVariableValue(String piece, char varType)
{
int colonPos;
String libName;
int secondColonPos;
String cellName;
int commaPos;
if (piece.length() == 0)
return null;
if (revision >= 1)
piece = unQuote(piece);
switch (varType)
{
case 'B': // Boolean
return new Boolean(piece.charAt(0)=='T' ? true : false);
case 'C': // Cell (should delay analysis until database is built!!!)
if (piece.length() == 0) return null;
colonPos = piece.indexOf(':');
if (colonPos < 0)
{
logError("Badly formed Cell (missing colon): " + piece);
break;
}
libName = piece.substring(0, colonPos);
LibId libId = idManager.newLibId(libName);
cellName = piece.substring(colonPos+1);
commaPos = cellName.indexOf(',');
if (commaPos >= 0) cellName = cellName.substring(0, commaPos);
return libId.newCellId(CellName.parseName(cellName));
case 'D': // Double
return Double.valueOf(piece);
case 'E': // Export (should delay analysis until database is built!!!)
colonPos = piece.indexOf(':');
if (colonPos < 0)
{
logError("Badly formed Export (missing library colon): " + piece);
break;
}
libName = piece.substring(0, colonPos);
libId = idManager.newLibId(libName);
secondColonPos = piece.indexOf(':', colonPos+1);
if (secondColonPos < 0)
{
logError("Badly formed Export (missing cell colon): " + piece);
break;
}
cellName = piece.substring(colonPos+1, secondColonPos);
CellId cellId = libId.newCellId(CellName.parseName(cellName));
String exportName = piece.substring(secondColonPos+1);
commaPos = exportName.indexOf(',');
if (commaPos >= 0) exportName = exportName.substring(0, commaPos);
return cellId.newPortId(exportName);
case 'F': // Float
return Float.valueOf(piece);
case 'G': // Long
return Long.valueOf(piece);
case 'H': // Short
return Short.valueOf(piece);
case 'I': // Integer
return Integer.valueOf(piece);
case 'L': // Library (should delay analysis until database is built!!!)
libName = piece;
commaPos = libName.indexOf(',');
if (commaPos >= 0) libName = libName.substring(0, commaPos);
return idManager.newLibId(libName);
case 'O': // Tool
String toolName = piece;
commaPos = toolName.indexOf(',');
if (commaPos >= 0) toolName = toolName.substring(0, commaPos);
Tool tool = Tool.findTool(toolName);
if (tool == null)
logError("Unknown Tool: " + piece);
return tool;
case 'P': // PrimitiveNodeId
colonPos = piece.indexOf(':');
if (colonPos < 0)
{
logError("Badly formed PrimitiveNode (missing colon): " + piece);
break;
}
String techName = piece.substring(0, colonPos);
TechId techId = idManager.newTechId(techName);
String nodeName = piece.substring(colonPos+1);
commaPos = nodeName.indexOf(',');
if (commaPos >= 0) nodeName = nodeName.substring(0, commaPos);
return techId.newPrimitiveNodeId(nodeName);
case 'R': // ArcProtoId
colonPos = piece.indexOf(':');
if (colonPos < 0)
{
logError("Badly formed ArcProto (missing colon): " + piece);
break;
}
techName = piece.substring(0, colonPos);
techId = idManager.newTechId(techName);
String arcName = piece.substring(colonPos+1);
commaPos = arcName.indexOf(',');
if (commaPos >= 0) arcName = arcName.substring(0, commaPos);
return techId.newArcProtoId(arcName);
case 'S': // String
if (revision >= 1) return piece;
if (piece.charAt(0) != '"')
{
logError("Badly formed string variable (missing open quote): " + piece);
break;
}
StringBuffer sb = new StringBuffer();
int len = piece.length();
int objectPos = 0;
while (objectPos < len)
{
objectPos++;
if (piece.charAt(objectPos) == '"') break;
if (piece.charAt(objectPos) == '^')
{
objectPos++;
if (objectPos <= len - 2 && piece.charAt(objectPos) == '\\' && piece.charAt(objectPos+1) == 'n')
{
sb.append('\n');
objectPos++;
continue;
}
}
sb.append(piece.charAt(objectPos));
}
return sb.toString();
case 'T': // TechId
techName = piece;
commaPos = techName.indexOf(',');
if (commaPos >= 0) techName = techName.substring(0, commaPos);
return idManager.newTechId(techName);
case 'V': // Point2D
int slashPos = piece.indexOf('/');
if (slashPos < 0)
{
logError("Badly formed Point2D variable (missing slash): " + piece);
break;
}
double x = Double.valueOf(piece.substring(0, slashPos));
double y = Double.valueOf(piece.substring(slashPos+1));
return new EPoint(x, y);
case 'Y': // Byte
return Byte.valueOf(piece);
}
return null;
}
private static double readDouble(String s) {
return s.length() > 0 ? Double.parseDouble(s) : 0;
}
private void logError(String message) {
String s = curReadFile + ", line " + lineReader.getLineNumber() + ", " + message;
errorLogger.logError(s, -1);
}
private void logWarning(String message) {
String s = curReadFile + ", line " + lineReader.getLineNumber() + ", " + message;
errorLogger.logWarning(s, null, -1);
}
private void logError(String message, CellId cellId) {
String s = curReadFile + ", line " + lineReader.getLineNumber() + ", " + message;
errorLogger.logError(s, cellId, -1);
}
}