📄 varcontext.java
字号:
/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: VarContext.java * * Copyright (c) 2003 Sun Microsystems and Static Free Software * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */package com.sun.electric.database.variable;import com.sun.electric.database.hierarchy.Cell;import com.sun.electric.database.hierarchy.Nodable;import com.sun.electric.database.text.Name;import com.sun.electric.database.text.TextUtils;import com.sun.electric.database.topology.NodeInst;import com.sun.electric.database.topology.PortInst;import com.sun.electric.tool.generator.layout.LayoutLib;import java.io.Serializable;import java.util.HashMap;import java.util.Map;import java.util.Stack;import java.util.Iterator;import java.util.List;import java.util.ArrayList;import java.util.regex.Pattern;import java.util.regex.Matcher;/** * VarContext represents a hierarchical path of NodeInsts. Its * primary use is to determine the value of variables which contain * Java code. In particular, the syntax @foo expands to P("foo") * which looks for the variable called foo in the NodeInst of this * VarContext. * * <p>A VarContext can also be used to recover the names of instances * along a hierarchical path. LE.getdrive() is an example of a * method that does this. * * <p> The VarContext object that represents the base top level is * VarContext.globalContext. You can get a new VarContext with an extra * NodeInst context attached by calling push(NodeInst) on the parent * context. You can get a VarContext with the most recent NodeInst * removed by calling pop(). Note that individual VarContexts are * immutable: you get new ones by calling push and pop; push and pop * do not edit their own VarContexts. * * <p>Retrieve a Variable by calling getVar(String name) on any * ElectricObject. * * <p>If the one knows that the Variable contains an object that * does not need to be evaluated, that object can be retrieved using * Variable.getObject(). * * <p>On the other hand, if the object may need to be evaluated because * it is type Java, TCL, or Lisp, then such evaluation may be hierarchy * dependent and one must call context.evalVar(variable). * * <p>Extra variables defined in the interpreter:<br> * * <p>Extra functions defined in the interpreter:<br> * * P(name) -- get the value of variable name on the most recent NodeInst. * Defaults to Integer(0).<br> * <p> * Methods PD(), PAR(), and PARD() are either gone or deprecated (RKao). Here * are what they used to do:<br> * * PD(name, default) -- get the value of variable name on the most recent * NodeInst. Defaults to default.<br> * PAR(name) -- get the value of variable name on any NodeInst, starting * with the most recent. Defaults to Integer(0).<br> * PARD(name, default) -- get the value of variable name on any NodeInst, * starting with the most recent. Defaults to default. * <P> * This class is thread-safe. */public class VarContext implements Serializable{ private static class ValueCache { private static class EvalPair { private final CodeExpression ce; private final Object info; public EvalPair(CodeExpression e, Object i) {ce=e; info=i;} public int hashCode() {return ce.hashCode() * info.hashCode();} public boolean equals(Object o) { if (!(o instanceof EvalPair)) return false; EvalPair ep = (EvalPair) o; return ce==ep.ce && info==ep.info; } } private final Map<EvalPair,Object> cache = new HashMap<EvalPair,Object>(); public synchronized boolean containsKey(CodeExpression ce, Object info) { return cache.containsKey(new EvalPair(ce, info)); } public synchronized Object get(CodeExpression ce, Object info) { return cache.get(new EvalPair(ce, info)); } public synchronized void put(CodeExpression ce, Object info, Object value) { EvalPair key = new EvalPair(ce, info); LayoutLib.error(cache.containsKey(key), "duplicate keys in ValueCache?"); cache.put(key, value); } } private static final Object FAST_EVAL_FAILED = new Object(); private final VarContext prev; private final Nodable ni; private transient ValueCache cache; // ------------------------ private methods ------------------------------- // For the global context. private VarContext() { this.ni = null; this.prev = this; this.cache = null; } private VarContext(Nodable ni, VarContext prev, boolean caching) { this.ni = ni; this.prev = prev; this.cache = caching ? new ValueCache() : null; } private Object readResolve() { return prev != this ? this : globalContext; } private void throwNotFound(String name) throws EvalException { throw new EvalException(name.replaceFirst("ATTR_", "")+" not found"); } private Object ifNotNumberTryToConvertToNumber(Object val) { if (val == null) return val; if (val instanceof Number) return val; try { Number d = TextUtils.parsePostFixNumber(val.toString()); val = d; } catch (java.lang.NumberFormatException e) { // just return original val object } return val; } /** If expression is a simple variable reference then return the name of * the variable; otherwise return null */ private String getSimpleVarRef(String expr) { final String pOpen = "P(\""; final int pOpenLen = pOpen.length(); final String pClose = "\")"; final int pCloseLen = pClose.length(); if (expr.startsWith(pOpen) && expr.endsWith(pClose)) { String varNm = expr.substring(pOpenLen, expr.length()-pCloseLen); return isValidIdentifier(varNm) ? varNm : null; } if (expr.startsWith("@")) { String varNm = expr.substring(1); return isValidIdentifier(varNm) ? varNm : null; } return null; } private boolean isValidIdentifier(String identifier) { final int len = identifier.length(); for (int i=0; i<len; i++) { if (!TextUtils.isLetterOrDigit(identifier.charAt(i))) return false; } return true; } private Object fastJavaVarEval(CodeExpression ce, Object info) throws EvalException { // Avoid re-computing the value if it is already in the cache. // Use "contains" because the value might be null. synchronized(this) { if (cache!=null && cache.containsKey(ce, info)) { return cache.get(ce, info); } } // Avoid calling bean shell if value is just a reference to another // variable. String expr = ce.getExpr(); String varNm = getSimpleVarRef(expr); if (varNm==null) return FAST_EVAL_FAILED; return lookupVarEval("ATTR_"+varNm); }// private void printValueCheckValue(Object value, Object checkValue) {// System.out.println("fast eval mismatch");// System.out.println(" fast value: "+value.toString());// System.out.println(" slow value: "+checkValue.toString());// }// private void checkFastValue(Object value, Variable var, Object info) throws EvalException {// if (value==FAST_EVAL_FAILED) return;// Object checkValue = EvalJavaBsh.evalJavaBsh.evalVarObject(var.getObject(), this, info);// if (value==null) {// LayoutLib.error(value!=checkValue, "fast eval null mismatch");// } else {// if (!value.equals(checkValue)) {// System.out.println("fast eval mismatch");// printValueCheckValue(value, checkValue);// LayoutLib.error(true, "fast eval mismatch");// }// }// } // ----------------------------- public methods --------------------------- /** * The blank VarContext that is the parent of all VarContext chains. */ public static final VarContext globalContext = new VarContext(); /** * get a new VarContext that consists of the current VarContext with * the given NodeInst pushed onto the stack */ public VarContext push(Nodable ni) { return new VarContext(ni, this, false); } /** * Push a new level onto the VarContext stack. If the value of any of * ni's parameters is requested and if the computation of that value * requires a call to the bean shell then the value is saved in this * VarContext so that future requests don't result in additional calls * to the bean shell. Note that this is implementing Call-By-Value * semantics whereas the non-caching VarContext implements Call-By-Name. * <p> * Be warned that there is no mechanism to automatically flush the caches * when the design is modified. The design MUST NOT change over the * life time of this VarContext. If the design might change then you should * use the non-caching VarContext. */ public VarContext pushCaching(Nodable ni) { return new VarContext(ni, this, true); } /** * get the VarContext that existed before you called push on it. * may return globalContext if the stack is empty. */ public VarContext pop() { return prev; } /** * Return the Node Instance that provides the context for the * variable evaluation for this level. */ public Nodable getNodable() { return ni; } /** * Return the PortInst that resides on the NodeInst that provides * the context. This is currently only useful for Highlighting. */ public PortInst getPortInst() { return null; } /** * Does deep comparison of two VarContexts. Matches * hierarchy traversal. Returns true if they both represent * the same hierarchy traversal, false otherwise. (Recursive method). * Does not compare PortInsts. * @param o the VarContext to compare against. * @return true if equal, false otherwise. */ public boolean equals(Object o) { if (!(o instanceof VarContext)) return false; VarContext c = (VarContext)o; if (this == c) return true; // both globalContext, or the same object // the following line doesn't work (R KAO, IvanM) //if (ni != c.getNodable()) return false; // compare nodeinsts if (ni == null || c.getNodable() == null) return ni == c.getNodable(); Cell c1 = ni.getParent(); Cell c2 = c.getNodable().getParent(); String name1 = ni.getName(); String name2 = c.getNodable().getName(); if (! ((c1 == c2) && (name1.equals(name2)))) return false; return prev.equals(c.pop()); // compare parents } /** * Get an iterator over the Nodables that describe this context. * This iterator starts from the top of the hierarchy, and goes down. * @return an iterator over the context path */ public Iterator<Nodable> getPathIterator() { Stack<Nodable> stack = new Stack<Nodable>(); VarContext context = this; while (context != VarContext.globalContext) { Nodable no = context.getNodable(); stack.push(no); context = context.pop(); } List<Nodable> path = new ArrayList<Nodable>(); while (!stack.isEmpty()) path.add(stack.pop()); return path.iterator(); } /** * Remove N levels of parent context from this VarContext. Returns * a new VarContext. This will return VarContext.globalContext * if the 'levels' is greater than or equal to the number of levels * in this context. * @param levels the number of levels of parent context to remove * @return a new VarContext */ public VarContext removeParentContext(int levels) { Stack<Nodable> nodes = new Stack<Nodable>(); VarContext acontext = this; while (acontext != VarContext.globalContext) { nodes.push(acontext.getNodable()); acontext = acontext.pop(); } for (int i=0; i<levels; i++) nodes.pop(); acontext = VarContext.globalContext; int size = nodes.size(); for (int i=0; i<size; i++) { Nodable no = nodes.pop(); acontext = acontext.push(no); } return acontext; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -