/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: JELIB2.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.CellBackup; import com.sun.electric.database.CellRevision; import com.sun.electric.database.ImmutableArcInst; import com.sun.electric.database.ImmutableCell; import com.sun.electric.database.ImmutableExport; import com.sun.electric.database.ImmutableIconInst; import com.sun.electric.database.ImmutableLibrary; import com.sun.electric.database.ImmutableNodeInst; import com.sun.electric.database.ImmutablePortInst; import com.sun.electric.database.LibraryBackup; import com.sun.electric.database.Snapshot; import com.sun.electric.database.constraint.Layout; 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.id.CellId; 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.text.Name; import com.sun.electric.database.variable.Variable; import com.sun.electric.technology.TechPool; import com.sun.electric.tool.Consumer; import com.sun.electric.tool.Job; import com.sun.electric.tool.JobException; import com.sun.electric.tool.MultiTaskJob; import com.sun.electric.tool.io.FileType; import com.sun.electric.tool.user.ErrorLogger; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.dialogs.OpenFile; import com.sun.electric.util.TextUtils; import com.sun.electric.util.math.DBMath; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Map; /** * This class reads files in new library file (.jelib) format to immutable database. */ public class JELIB2 { LibId libId; JelibParser parser; public LibraryBackup libBackup; ArrayList<CellRevision> cellRevisions = new ArrayList<CellRevision>(); public ArrayList<CellBackup> cellBackups = new ArrayList<CellBackup>(); public JELIB2(LibId libId, JelibParser parser) { this.libId = libId; this.parser = parser; } public CellRevision[] getCellRevisions() { return cellRevisions.toArray(new CellRevision[cellRevisions.size()]); } public boolean instantiate(TechPool techPool, boolean primitiveBounds) throws JelibException { ImmutableLibrary l = ImmutableLibrary.newInstance(libId, parser.fileURL, parser.version); for (Variable var : parser.libVars) { l = l.withVariable(var); } boolean modified = false; for (JelibParser.CellContents cc : parser.allCells.values()) { CellId cellId = cc.cellId; ImmutableCell c = ImmutableCell.newInstance(cellId, cc.creationDate).withGroupName(cc.groupName).withRevisionDate(cc.revisionDate).withTechId(cc.techId); int flags = 0; if (cc.expanded) { flags |= Cell.WANTNEXPAND; } if (cc.allLocked) { flags |= Cell.NPLOCKED; } if (cc.instLocked) { flags |= Cell.NPILOCKED; } if (cc.cellLib) { flags |= Cell.INCELLLIBRARY; } if (cc.techLib) { flags |= Cell.TECEDITCELL; } c = c.withFlags(flags); for (Variable var : cc.vars) { if (var.getTextDescriptor().isParam()) { c = c.withParam(var); } else { c = c.withVariable(var); } } ImmutableNodeInst[] nodes = new ImmutableNodeInst[cc.nodes.size()]; for (int nodeId = 0; nodeId < nodes.length; nodeId++) { JelibParser.NodeContents nc = cc.nodes.get(nodeId); ImmutableNodeInst n = ImmutableNodeInst.newInstance(nodeId, nc.protoId, Name.findName(nc.nodeName), nc.nameTextDescriptor, nc.orient, nc.anchor, nc.size, nc.flags, nc.techBits, nc.protoTextDescriptor); for (Variable var : nc.vars) { String origVarName = var.getKey().getName(); if (origVarName.startsWith("ATTRP")) { // the form is "ATTRP_portName_variableName" with "\" escapes StringBuilder portName = new StringBuilder(); 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) { PortProtoId ppId = nc.protoId.newPortId(portName.toString()); ImmutablePortInst pi = n.getPortInst(ppId); var = var.withVarKey(Variable.newKey(varName)); n = n.withPortInst(ppId, pi.withVariable(var)); continue; } } if (n instanceof ImmutableIconInst && var.getTextDescriptor().isParam()) { n = ((ImmutableIconInst) n).withParam(var); } else { n = n.withVariable(var); } } nc.n = nodes[nodeId] = n; } ImmutableArcInst[] arcs = new ImmutableArcInst[cc.arcs.size()]; for (int arcId = 0; arcId < arcs.length; arcId++) { JelibParser.ArcContents ac = cc.arcs.get(arcId); ImmutableArcInst a = ImmutableArcInst.newInstance(arcId, ac.arcProtoId, Name.findName(ac.arcName), ac.nameTextDescriptor, ac.tailNode.n.nodeId, ac.tailPort, ac.tailPoint, ac.headNode.n.nodeId, ac.headPort, ac.headPoint, DBMath.lambdaToGrid(0.5 * ac.diskWidth), ac.angle, ac.flags); for (Variable var : ac.vars) { a = a.withVariable(var); } arcs[arcId] = a; } ImmutableExport[] exports = new ImmutableExport[cc.exports.size()]; for (int exportIndex = 0; exportIndex < exports.length; exportIndex++) { JelibParser.ExportContents ec = cc.exports.get(exportIndex); String exportName = ec.exportUserName != null ? ec.exportUserName : ec.exportId.externalId; ImmutableExport e = ImmutableExport.newInstance(ec.exportId, Name.findName(exportName), ec.nameTextDescriptor, ec.originalNode.n.nodeId, ec.originalPort, ec.alwaysDrawn, ec.bodyOnly, ec.ch); for (Variable var : ec.vars) { e = e.withVariable(var); } exports[exportIndex] = e; } CellRevision cellRevision = new CellRevision(c); CellBackup cellBackup = techPool != null ? CellBackup.newInstance(c, techPool) : null; try { if (techPool != null) { cellBackup = cellBackup.with(c, nodes, arcs, exports, techPool).withoutModified();; } else { cellRevision = cellRevision.with(c, nodes, arcs, exports); } } catch (IllegalArgumentException e) { Arrays.sort(nodes, new Comparator<ImmutableNodeInst>() { public int compare(ImmutableNodeInst n1, ImmutableNodeInst n2) { return TextUtils.STRING_NUMBER_ORDER.compare(n1.name.toString(), n2.name.toString()); } }); Arrays.sort(arcs, new Comparator<ImmutableArcInst>() { public int compare(ImmutableArcInst a1, ImmutableArcInst a2) { return TextUtils.STRING_NUMBER_ORDER.compare(a1.name.toString(), a2.name.toString()); } }); Arrays.sort(exports, new Comparator<ImmutableExport>() { public int compare(ImmutableExport e1, ImmutableExport e2) { return TextUtils.STRING_NUMBER_ORDER.compare(e1.name.toString(), e2.name.toString()); } }); if (techPool != null) { cellBackup = cellBackup.with(c, nodes, arcs, exports, techPool); } else { cellRevision = cellRevision.with(c, nodes, arcs, exports); } modified = true; } if (techPool != null) { if (primitiveBounds) { cellBackup.getPrimitiveBounds(); } cellBackups.add(cellBackup); cellRevisions.add(cellBackup.cellRevision); } else { cellRevisions.add(cellRevision); } } libBackup = new LibraryBackup(l, modified, LibId.NULL_ARRAY); parser = null; return true; } public void check() throws JelibException { for (CellRevision cellRevision : cellRevisions) { String protoName = cellRevision.d.cellId.cellName.getName(); for (int i = 0; i < protoName.length(); i++) { char chr = protoName.charAt(i); if (Character.isWhitespace(chr) || chr == ':' || chr == ';' || chr == '{' || chr == '}') { throw new JelibException(); } } } } /* * TODO * JelibParser lost bad CellNames, * JelibParset lost duplicate CellNames * check CellGroups: 1) same name => same group; 2) Rules for group name * check Variables and Params (see LibraryFiles.realizeCellParameters). * check ImmutableNodes * check SizeCorrector is zero * check ATTRP_ is absent */ private static class JelibException extends Exception { private JelibException() { super(); } } public static void newJelibReader(boolean instantiate, boolean doBackup, boolean getPrimitiveBounds, boolean doSnapshot, final boolean doDatabase) { String fileName = OpenFile.chooseInputFile(FileType.LIBFILE, "Top library"); if (fileName == null) { return; } URL externalURL = TextUtils.makeURLToFile(fileName); if (externalURL == null) { return; } if (!TextUtils.URLExists(externalURL, null)) { return; } Consumer<Snapshot> consumer = new Consumer<Snapshot>() { public void consume(Snapshot snapshot) { if (doDatabase && snapshot != null) { new JelibReaderCommitJob(snapshot).startJobOnMyResult(); } } }; new JelibReaderMultiTaskJob(externalURL, instantiate, doBackup, getPrimitiveBounds, doSnapshot, doDatabase, false, consumer).startJob(); } private static class JelibReaderMultiTaskJob extends MultiTaskJob<URL, JELIB2, Snapshot> { private final boolean instantiate; private final boolean doBackup; private final boolean getPrimitiveBounds; private final boolean doSnapshot; private final boolean doDatabase; private final boolean check; private final URL topLibFile; private String mainLibDirectory; private transient IdManager idManager; private transient HashSet<LibId> externalLibraries; private JelibReaderMultiTaskJob(URL topLibFile, boolean instantiate, boolean doBackup, boolean getPrimitiveBounds, boolean doSnapshot, boolean doDatabase, boolean check, Consumer<Snapshot> c) { super("JelibReaderMultiTaskJobLight", null, c); this.topLibFile = topLibFile; this.instantiate = instantiate; this.doBackup = doBackup; this.getPrimitiveBounds = getPrimitiveBounds; this.doSnapshot = doSnapshot; this.doDatabase = doDatabase; this.check = check; String topLibName = TextUtils.getFileNameWithoutExtension(topLibFile); mainLibDirectory = TextUtils.getFilePath(topLibFile); FileType type = FileType.findTypeByExtension(TextUtils.getExtension(topLibFile)); if (type == FileType.DELIB) { mainLibDirectory = mainLibDirectory.replaceAll(topLibName + "." + type.getFirstExtension(), ""); } } /** * This abstract method split large computation into smaller task. * Smaller tasks are identified by TaskKey class. * Each task is scheduled by startTask method. * @throws com.sun.electric.tool.JobException */ @Override public void prepareTasks() throws JobException { if (false) { idManager = new IdManager(); } else { idManager = IdManager.stdIdManager; } externalLibraries = new HashSet<LibId>(); if (check) { for (Iterator<Library> it = Library.getLibraries(); it.hasNext();) { Library lib = it.next(); URL libFile = lib.getLibFile(); if (libFile == null) { continue; } startTask(libFile.toString(), libFile); } } else { externalLibraries.add(idManager.newLibId(TextUtils.getFileNameWithoutExtension(topLibFile))); startTask(topLibFile.toString(), topLibFile); } } /** * This abtract methods performs computation of each task. * @param taskKey task key which identifies the task * @return result of task computation * @throws com.sun.electric.tool.JobException */ @Override public JELIB2 runTask(URL libFile) throws JobException { LibId libId = idManager.newLibId(TextUtils.getFileNameWithoutExtension(libFile)); Date startDate = new Date(); FileType fileType = FileType.findTypeByExtension(TextUtils.getExtension(libFile)); JELIB2 jelib2 = null; ErrorLogger errorLogger = ErrorLogger.newInstance(libId.toString()); TechPool techPool = doBackup ? getTechPool() : null; try { JelibParser parser = JelibParser.parse(libId, libFile, fileType, false, errorLogger); for (Map.Entry<LibId, String> e : parser.externalLibIds.entrySet()) { LibId externallibId = e.getKey(); String libFileName = e.getValue(); if (Library.findLibrary(libId.libName) != null) { continue; } startTask(externallibId, libFileName, fileType); } jelib2 = new JELIB2(libId, parser); boolean ok = false; if (instantiate) { ok = jelib2.instantiate(techPool, getPrimitiveBounds); } Date stopDate = new Date(); System.out.println("Parsing " + errorLogger.getInfo() + " " + ok + " " + startDate + " " + stopDate + " " + Thread.currentThread().getName()); } catch (Exception e) { System.out.println("Parsing " + libId + " caused an exception " + e); e.printStackTrace(); } if (check) { checkLibrary(libId, jelib2); } return jelib2; } private void startTask(LibId externalLibId, String libFileName, FileType fileType) { synchronized (externalLibraries) { if (externalLibraries.contains(externalLibId)) { return; } } URL fileURL = LibraryFiles.searchExternalLibraryFromFilename(mainLibDirectory, libFileName, fileType); if (fileURL == null) { return; } synchronized (externalLibraries) { if (!externalLibraries.add(externalLibId)) { return; } } startTask(fileURL.toString(), fileURL); } private void checkLibrary(LibId libId, JELIB2 jelib2) { EDatabase database = getDatabase(); CellRevision[] cellRevisions = jelib2.getCellRevisions(); Library lib = database.getLib(libId); assert lib.getNumCells() == cellRevisions.length; for (CellRevision cellRevision1 : cellRevisions) { ImmutableCell c1 = cellRevision1.d; CellId cellId = c1.cellId; CellRevision cellRevision2 = database.getCell(cellId).backup().cellRevision; ImmutableCell c2 = cellRevision2.d; assert c1.equalsExceptVariables(c2) && c1.equalsVariables(c2); assert cellRevision1.nodes.size() == cellRevision2.nodes.size(); assert cellRevision1.nodes.size() == cellRevision2.nodes.size(); for (int nodeId = 0; nodeId < cellRevision1.nodes.size(); nodeId++) { ImmutableNodeInst n1 = cellRevision1.nodes.get(nodeId); ImmutableNodeInst n2 = cellRevision2.nodes.get(nodeId); assert n1.equalsExceptVariables(n2) && n1.equalsVariables(n2); } assert cellRevision1.arcs.size() == cellRevision2.arcs.size(); for (int arcId = 0; arcId < cellRevision1.arcs.size(); arcId++) { ImmutableArcInst a1 = cellRevision1.arcs.get(arcId); ImmutableArcInst a2 = cellRevision2.arcs.get(arcId); assert a1.equalsExceptVariables(a2) && a1.equalsVariables(a2); } assert cellRevision1.exports.size() == cellRevision2.exports.size(); for (int exportId = 0; exportId < cellRevision1.exports.size(); exportId++) { ImmutableExport e1 = cellRevision1.exports.get(exportId); ImmutableExport e2 = cellRevision2.exports.get(exportId); assert e1.equalsExceptVariables(e2) && e1.equalsVariables(e2); } } } /** * This abtract method combines task results into final result. * @param taskResults map which contains result of each completed task. * @return final result which is obtained by merging task results. * @throws com.sun.electric.tool.JobException */ @Override public Snapshot mergeTaskResults(Map<URL, JELIB2> taskResults) throws JobException { if (!doSnapshot) { return null; } ArrayList<LibraryBackup> libBackups = new ArrayList<LibraryBackup>(); ArrayList<CellBackup> cellBackups = new ArrayList<CellBackup>(); if (doDatabase) { Snapshot oldSnapshot = getDatabase().backup(); libBackups.addAll(oldSnapshot.libBackups); cellBackups.addAll(oldSnapshot.cellBackups); } for (JELIB2 jelib2 : taskResults.values()) { LibraryBackup libBackup = jelib2.libBackup; int libIndex = libBackup.d.libId.libIndex; while (libIndex >= libBackups.size()) { libBackups.add(null); } libBackups.set(libIndex, libBackup); for (CellBackup cellBackup : jelib2.cellBackups) { int cellIndex = cellBackup.cellRevision.d.cellId.cellIndex; while (cellIndex >= cellBackups.size()) { cellBackups.add(null); } cellBackups.set(cellIndex, cellBackup); } } try { Snapshot snapshot = idManager.getInitialSnapshot().with(null, getEnvironment(), cellBackups.toArray(new CellBackup[cellBackups.size()]), libBackups.toArray(new LibraryBackup[libBackups.size()])); return snapshot; } catch (Exception e) { e.printStackTrace(System.out); getUserInterface().showErrorMessage("Error loading libraries", "New JELIB Reader"); return null; } } } private static class JelibReaderCommitJob extends Job { private transient Snapshot newSnapshot; protected JelibReaderCommitJob(Snapshot newSnapshot) { super("JelibReaderCommit", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER); this.newSnapshot = newSnapshot; } @Override public boolean doIt() { Layout.changesQuiet(true); EDatabase database = getDatabase(); database.lowLevelSetCanUndoing(true); try { database.undo(newSnapshot); } finally { database.lowLevelSetCanUndoing(false); } return true; } } }