scanengine.java
来自「linux下建立JAVA虚拟机的源码KAFFE」· Java 代码 · 共 861 行 · 第 1/2 页
JAVA
861 行
/* ScanEngine.java -- Scans the input and generates an object tree that can be written as XML. Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath 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 2, or (at your option) any later version. GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */package gnu.java.beans.encoder;import java.beans.Expression;import java.beans.Statement;import java.io.OutputStream;import java.lang.reflect.Array;import java.util.HashMap;import java.util.IdentityHashMap;import java.util.List;import java.util.Stack;/** <p>The <code>ScanEngine</code> is the main class of the backend of the * XML persistence algorithm. It scans {@link java.beans.Expression} and * {@link java.beans.Statement} instances and some raw objects via the * {@link #writeObject} method and feeds it to a state machine. The * state machine then constructs and object tree which is finally * written as XML by a {@link Writer} implementation.</p> * * <p>How does it work?</p> * <p>The <code>ScanEngine</code> sits below the {@link java.beans.XMLEncoder} * class and is called by it exclusively. The <code>XMLEncoder</code> sends * interpretive data by invoking {@link #writeExpression}, {@link #writeStatement} * and {@link #writeObject}. The invocations of <code>writeExpression</code> and * <code>writeStatement</code> are usually nested into each other and provide * more information then necessary to generate the XML representation. * Furthermore the meaning of certain <code>Expressions</code> differs * depending on the enclosing elements or the inner elements have to be * simply discarded.</p> * * <p>To cope with this state dependant nature the <code>ScanEngine</code> * contains a state machine which is programmed statically (no adjustments are * needed, all <code>ScanEngine</code> engines use the same setup). The * <code>ScanEngine</code>'s job is to decode the <code>Expression</code>s, * <code>Statement</code>s and certain objects (namely <code>String</code>, * <code>null</code> objects and instances which are repeatedly provided to * the encoder) into 13 low-level (event) methods, which denote the meaning of the * argument. For example an <code>Expression</code> can be an array * instantiation which provokes a call to {@link arrayInstantiation} or * it can be a class resolution leading to a call to {@link #classResolution}. * For the state machione the 13 methods are the distinct way to transit * from one state to another. Whenever the <code>ScanEngine</code> calls * one of the event methods the current's state successor for that event * is fetched from the state machine configuration, the successpr becomes * the current state and then the event method is called in the new current * state. The last step allows the state instance to do something meaningful * to the object tree.</p> * * <p>The state machine knows the concept of returning to the previous * state. This is done using a stack of states which is popped every * time a call to <code>writeStatement</code>, <code>writeExpression</code> * in the <code>XMLEncoder</code> ends by calling the {@link #end} method. * Note that due to the inheritance relationship of <code>Encoder</code> * and <code>XMLEncoder</code> it is impossible for the * <code>ScanEngine</code> itself to decide when an expression or statement * ended. This can only be done in case of {@link #writeObject} calls because * they are not nested.</p> * * <p>When the XML persistence mechanism reaches an object twice (and more) * it should generate an XML element using the "idref" attribute and add * an "id" attribute to its first instantiation. This complicates things a bit * because the first instantiation will always be part of the object tree * as some {@link gnu.java.beans.encoder.elements.Element} subclass instance when the * second and further objects accesses are written. Therefore the {@link ObjectId} * class was introduced which is shared between all the object tree elements * and has the notion of an "unused" state meaning that no identification * is needed. The relationship between an object and its <code>ObjectId</code> * instance is stored in the <code>ScanEngine</code> and gets cleared whenever * the {@link #flush} method is called. This method also writes the currently * built object tree and generates the XML representation.</p> * * @author Robert Schuster (robertschuster@fsfe.org) */public class ScanEngine{ /** Change this to true to let the ScanEngine print state transition * information. */ boolean DEBUG = false; /** * Stores the scanner engine states as values and their names as keys. */ HashMap states = new HashMap(); /** * Stores former scanner state and makes it possible to come back to them. */ Stack parents = new Stack(); /** * The currently active scanner state. */ ScannerState current; /** * The root of an object tree that is later written to XML. */ Root root; /** * The Writer used to generate the XML output. */ Writer writer; /** Stores the relationship between objects and their {@link ObjectId} instance. */ IdentityHashMap objects = new IdentityHashMap(); public ScanEngine(OutputStream os) { // TODO: Provide another Writer implementation (e.g. one that does not use // the XML APIs at all). writer = new StAXWriter(os); root = new Root(); final ScannerState start = current = new GenericScannerState(root);; ScannerState conf; // Use the ReportingScannerState to debug serialization issues. register(ScannerState.DEFAULT_STATE_NAME, new IgnoringScannerState()); register("start", start); // Special dead-end state where all transitions are ignored. register("ignoreAll", new IgnoringScannerState()) .setDefaultSuccessor("ignoreAll"); // Object reference, string reference, null object start.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "simple"); start.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "simple"); start.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "simple"); register("simple", new GenericScannerState(root)) .setDefaultSuccessor("ignoreAll"); // Class resolution. start.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "classRes0"); register("classRes0", new GenericScannerState(root)).setDefaultSuccessor("ignoreAll"); // Object instantiation. start.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, "newObj0"); conf = register("newObj0", new GenericScannerState(root)); conf.setDefaultSuccessor("ignoreAll"); // Simply use the start state to encode method invocations inside of // objects. conf.putSuccessor(ScannerState.TRANSITION_METHOD_INVOCATION, "start"); // Primitive instantiations. start.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "newPrimitive0"); register("newPrimitive0", new GenericScannerState(root)).setDefaultSuccessor("ignoreAll"); // Object arrays use the ARRAY_GET transition to create setting the // array values. start.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, "newObjectArray"); conf = register("newObjectArray", new GenericScannerState(root)); conf.putSuccessor(ScannerState.TRANSITION_ARRAY_GET, "newOArrayGet"); conf.putSuccessor(ScannerState.TRANSITION_ARRAY_SET, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "ignoreAll"); // Get here when a value is set in the array. register("newOArrayGet", conf = new GenericScannerState(root)); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "newOArrayGet_ignoreFirstInteger"); // "newArrayGet_ignoreFirstInteger" is set up mostly identical like the "start" // state. Otherwise things would not behave the same when done inside // arrays. conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "simple"); conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "simple"); conf.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "simple"); conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "classRes0"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, "newObj0"); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, "newPrimitiveArray"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, "newObjectArray"); conf = register("newOArrayGet_ignoreFirstInteger", new GenericScannerState(root, 1)); // In non-int primitive arrays class resolutions can happen // but they should be ignored. conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); // Spurious object and string references occur when setting array // elements. This suppresses them. conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); conf.setDefaultSuccessor("start"); // Primitive arrays use the ARRAY_SET transition to create setting the // array values. This turned out to be the only working solution. // When primitive arrays were handled by ARRAY_GET the values in boolean // arrays were always skipped. start.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, "newPrimitiveArray"); conf = register("newPrimitiveArray", new GenericScannerState(root)); conf.putSuccessor(ScannerState.TRANSITION_ARRAY_GET, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_ARRAY_SET, "newPArraySet"); conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "ignoreAll"); conf = register("newPArraySet", new GenericScannerState(root)); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "newPArraySet_ignoreFirstInteger"); // Primitive arrays ignore all kinds of non-primitive object information. conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ingoreAll"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, "ignoreAll"); conf = register("newPArraySet_ignoreFirstInteger", new GenericScannerState(root, 1)); // In non-int primitive arrays class resolutions can happen // but they should be ignored. conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); // Spurious object and string references occur when setting array // elements. This suppresses them. conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "ignoreAll"); conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); conf.setDefaultSuccessor("start"); } /** Registers a <code>ScannerState</code> under a certain name. * * @param name Name of the state * @param state The <code>ScannerState</code> instance. * @return The second argument. */ private ScannerState register(String name, ScannerState state) { state.init(name); states.put(name, state); return state; } /** Generates or returns an id for the given object which can be activated * later if the object is suitable. * * <p>Objects are unsuitable if they are an instance of a primitive wrapper * or String.</p> * * @param value The object to retrieve an id for. * @return The id for the object or <code>null</code>. */ private ObjectId retrieveId(Object value) { Class valueClass = value.getClass(); ObjectId id = null; // Although multiple accesses to Class objects are not handled // through ids we generate one for them, too. This allows us to detect // second time references to such objects in the writeObject method // and handle them in a special way. if (valueClass != String.class && valueClass.getSuperclass() != Number.class && valueClass != Boolean.class) { if ((id = (ObjectId) objects.get(value)) == null) { id = new ObjectId(valueClass); objects.put(value, id); } } return id; } /** Scans the argument and calls one of event methods. See * the introduction of this class for details. * * @param expr The expression to serialize. */ public void writeExpression(Expression expr) { String methodName = expr.getMethodName(); Object[] args = expr.getArguments(); Object target = expr.getTarget(); Object value = null; try { value = expr.getValue(); } catch (Exception e) { throw (InternalError) new InternalError( "The Expression's value should be available at this point.") .initCause(e); } // TODO: What if the value is null? ObjectId id; Class valueClass = value.getClass(); if (target == Array.class) { if (methodName.equals("newInstance")) { id = retrieveId(value); Class ct = (Class) args[0]; if (ct.isPrimitive() || ct == Boolean.class || ct == Byte.class || ct == Short.class || ct == Integer.class || ct == Long.class || ct == Float.class || ct == Double.class) primitiveArrayInstantiation(ct.getName(), args[1].toString(), id); else objectArrayInstantiation(ct.getName(), args[1].toString(), id); return; } else if (methodName.equals("get")) { arrayGet(args[1].toString()); // The encoder does not call the ScanEngine // when an object is serialized that we already know. // We test for this situation and insert the object reference // manually. // Since there is already a workaround for the Class class // in writeObject we have to except it from this behavior. id = (ObjectId) objects.get(value); if (id != null && valueClass != Class.class) { objectReference(id); end(); } return; } else if (methodName.equals("set")) { arraySet(args[1].toString()); return; } } id = retrieveId(value); if (target instanceof Class) { if (methodName.equals("new")) { Class targetClass = (Class) target; // All primitive types have short-hand forms for their // constructors. if (valueClass == Boolean.class) primitiveInstantiation("boolean", args[0].toString()); else if (valueClass == Byte.class) primitiveInstantiation("byte", args[0].toString()); else if (valueClass == Short.class) primitiveInstantiation("short", args[0].toString()); else if (valueClass == Integer.class) primitiveInstantiation("int", args[0].toString()); else if (valueClass == Long.class) primitiveInstantiation("long", args[0].toString()); else if (valueClass == Float.class)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?