package com.sun.electric.tool.drc; import com.sun.electric.database.geometry.EPoint; import com.sun.electric.database.geometry.PolyBase; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.tool.Job; import com.sun.electric.tool.user.ErrorLogger; import java.awt.geom.Point2D; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Reads RDB error database, typically used for reporting antenna rule * violations. A read of a new file accumulates to the same error report, * as typically multiple RDB databases are generated for a single * antenna rule drc run. */ public class CalibreRdbErrors { private int scale; private String topCellName; private Cell topCell; private ErrorLogger logger; private BufferedReader in; private int lineno; private Map<Cell,String> mangledNames; private int errorCount; private static final String spaces = "[\\s\\t ]+"; /** * Create a new error object to read errors in from file * This creates a new error logger instance to add errors to, * but you must call {@link CalibreRdbErrors#termLogging(boolean)} * to display the errors in the error log. */ public CalibreRdbErrors() { logger = ErrorLogger.newInstance("Calibre Antenna DRC Errors"); errorCount = 0; } /** * Import errors from an RDB database to the current logger * @param filename the filename to import errors from * @param mangledNames mangled GDS cell names */ public void importErrors(String filename, Map<Cell,String> mangledNames) { lineno = 1; this.mangledNames = mangledNames; try { System.out.println("Reading RDB file "+filename); FileReader reader = new FileReader(filename); in = new BufferedReader(reader); // read header String line = in.readLine(); if (line == null) return; String [] parts = line.trim().split(spaces); if (parts.length != 2) { System.out.println("Error: Invalid header in RDB file at line "+lineno+": "+line); return; } topCellName = parts[0]; topCell = CalibreDrcErrors.getCell(topCellName, mangledNames); if (topCell == null) { System.out.println("Error: Cell '"+topCellName+"' specified in file does not exist in current hierarchy: "+filename); return; } try { scale = Integer.valueOf(parts[1]).intValue(); } catch (NumberFormatException e) { System.out.println("Warning: Invalid scale value "+parts[1]+" at line "+lineno); scale = 1000; } lineno++; boolean more = true; while (more) { more = readRuleViolation(); } in.close(); } catch (IOException e) { System.out.println("Error importing RDB database: "+e.getMessage()); return; } } /** * Finish reading errors and display error logger * @param explain true to pop up display to user */ public void termLogging(boolean explain) { logger.termLogging(explain); Job.getUserInterface().showInformationMessage("Calibre Antenna DRC Imported "+errorCount+" errors", "Calibre Antenna DRC"); } /** * Read a violation from the current opened database file * @return false if nothing left to read * @throws IOException on read exception */ private boolean readRuleViolation() throws IOException { if (in == null) return false; double lambdaScale = topCell.getTechnology().getScale() / 1000; // first line is rule_name::some value String line = in.readLine(); if (line == null) return false; int ruleEnd = line.indexOf("::"); if (ruleEnd < 0) { System.out.println("Error: expected :: in rule name line at "+lineno+": "+line); return false; } String rule = line.substring(0, ruleEnd); lineno++; // second line is date, ignore line = in.readLine(); if (line == null) { System.out.println("Error: premature end of file at line "+lineno); return false; } lineno++; // third line is rule check code line = in.readLine(); if (line == null) { System.out.println("Error: premature end of file at line "+lineno); return false; } String ruleText = line; boolean ruleTextAnnotatedWithAR = false; lineno++; // from now on, line is either a list of name=value pairs, // or it is a polygon (p) or edge (e) geometry list List<EPoint> lineList = new ArrayList<EPoint>(); List<PolyBase> polyList = new ArrayList<PolyBase>(); List<Property> properties = new ArrayList<Property>(); StringBuffer propLines = new StringBuffer(); Cell cell = null; while (true) { in.mark(200); line = in.readLine(); if (line == null) break; if (line.startsWith(rule)) { in.reset(); break; // next error found, report this error and return true } if (line.contains("=")) { String parts [] = line.trim().split(spaces); String commonprefix = ""; int i=0; if (!parts[0].contains("=")) { // this is a common prefix to all names in the subsequent name=value pairs commonprefix = parts[0]+" "; i++; } for (; i<parts.length; i++) { String [] namevalue = parts[i].split("="); if (namevalue.length != 2) continue; properties.add(new Property(commonprefix+namevalue[0], namevalue[1])); /* if (namevalue[0].equalsIgnoreCase("cell")) { cell = CalibreDrcErrors.getCell(namevalue[1], mangledNames); if (cell == null) continue; lambdaScale = cell.getTechnology().getScale() / 1000; } */ } propLines.append(line); propLines.append("\n"); lineno++; } else if (line.startsWith("p")) { String [] parts = line.trim().split(spaces); if (parts.length != 3) { System.out.println("Error parsing polygon list at line"+lineno+": "+line); return false; } int count = 0; try { count = Integer.parseInt(parts[2]); } catch (NumberFormatException e) { System.out.println("Error: expected number as third argument in polygon at line "+lineno+": "+parts[2]); return false; } Point2D[] points = new Point2D[count]; for (int i=0; i<count; i++) { line = in.readLine(); if (line == null) { System.out.println("Error: premature end of file at line "+lineno); return false; } if (line.startsWith("AR")) { i=-1; // reset, AR line seems to be new thing for newer version of Calibre. if (!ruleTextAnnotatedWithAR) { ruleText = line+"; "+ruleText; ruleTextAnnotatedWithAR = true; } continue; } lineno++; String [] coords = line.trim().split(spaces); if (coords.length != 2) { System.out.println("Error: expected two fields for poly coord at "+lineno+": "+line); return false; } double x=0, y=0; try { x = Double.parseDouble(coords[0])/scale/lambdaScale; y = Double.parseDouble(coords[1])/scale/lambdaScale; } catch (NumberFormatException e) { System.out.println("Error: polygon coordinates should be numbers at line "+lineno+": "+line); return false; } points[i] = new Point2D.Double(x, y); } polyList.add(new PolyBase(points)); } else if (line.startsWith("e")) { System.out.println("Error: edge spec not supported yet, didn't have example. Please tell JKG. Thanks"); return false; } } // Log error StringBuffer props = new StringBuffer(); for (Property prop : properties) { props.append(prop.toString()); } if (cell == null) cell = topCell; logger.logMessageWithLines(ruleText+"\n"+propLines.toString(), polyList, lineList, cell, 0, true); errorCount++; return true; } private static class Property { private final String name; private final String value; private Property(String name, String value) { this.name = name; this.value = value; } public String toString() { return name+"="+value; } } }