All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.enhydra.xml.xmlc.dom.generic.BuildMethodMappings Maven / Gradle / Ivy

The newest version!
/*
 * Enhydra Java Application Server Project
 * 
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License on
 * the Enhydra web site ( http://www.enhydra.org/ ).
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
 * the License for the specific terms governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Enhydra Application Server is Lutris
 * Technologies, Inc. The Enhydra Application Server and portions created
 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * $Id: BuildMethodMappings.java,v 1.2 2005/01/26 08:29:24 jkjome Exp $
 */

package org.enhydra.xml.xmlc.dom.generic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

import org.enhydra.xml.xmlc.XMLCError;
import org.enhydra.xml.xmlc.codegen.JavaLang;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Class to calculate which node are to have build methods created for
 * them based on trying to optimize the amount of code (create-cost) in each
 * build methods without overflowing the maximum create-cost.
 * 

* Build methods are normally rooted at a subtree. Costs are adjusted by * moving the creation of children into sub-build methods. This leaves just * the cost of calling the build method. If the build cost is till too high * after moving all children into build methods, the methods are chained * together to handle creating the children. *

* Note that the tree is a bit more comple than just the child nodes. * Elements have attributes and DocumentTypes have lists of nodes. * Collectively, these are refered to as contained nodes. *

* The advantage of this object determining where build methods are created in * a seperate traversal from creating the build methods is that the * determination is best down from the bottom up, while creating the methods * is done top-down. The seperation also keeps the code cleaner and easier to * understand. The cost is building an intermediate table. */ final class BuildMethodMappings { /** * Create-cost to call a build-method. */ public static final int BUILD_METHOD_CALL_CREATE_COST = 1; /** * Create-cost to create an element node. Slightly higher due * to possibility of initializing access methods. */ public static final int CREATE_ELEMENT_CREATE_COST = 2; /** * Create-cost to create other nodes. */ public static final int CREATE_NODE_CREATE_COST = 1; /** * Document we are associated with. */ private Document fDocument; /** * Maximum creation-cost that a single build method can handle. */ private int fMaxCreateCostPerBuildMethod; /* * Table of node record. */ private HashMap fNodeRecordTable = new HashMap(); /** * Node record. */ private class Record { /** The node we are associated with. */ private Node fNode; /** * The total cost to create this node and its children in this build * method. It includes calls to other build methods, but not the cost * in other build methods. */ private int fCreateCost; /** * The cost to the parent to create this node. If the create cost is * rooted at a method, then this is simply the cost to call the build * method. If the node is not rooted at a method, the is the total * cost to create the node. */ private int fParentCreateCost; /** Should this node be at the root of a build method? */ private boolean fMethodRoot; /** Should the children be created in a chained method? */ private boolean fChainedChildrenMethods; /** * Constructor. Initialize base costs. */ public Record(Node node) { fNode = node; fCreateCost = getNodeTypeCost(node); fParentCreateCost = fCreateCost; // Add self to table fNodeRecordTable.put(node, this); } /* Mark this as a method root */ public void makeMethodRoot() { fParentCreateCost = BUILD_METHOD_CALL_CREATE_COST; fMethodRoot = true; } /* Mark this as a having it's children created in chained methods */ public void makeChainedChildrenMethods () { fChainedChildrenMethods = true; } /** Determine if this can be a method root */ public boolean canBeMethodRoot() { return BuildMethodMappings.this.canBeMethodRoot(fNode); } /** * Adjust the creation cost, this also changes the parent cost * if it's not a method root. It doesn't change the parent record, * as this is down during the bottom-up build of the tree. */ public void adjustCreateCost(int amount) { fCreateCost += amount; if (!fMethodRoot) { fParentCreateCost += amount; } } /** Get the node */ public Node getNode() { return fNode; } /** Get the create cost */ public int getCreateCost() { return fCreateCost; } /** Get the cost to the parent to create this node */ public int getParentCreateCost() { return fParentCreateCost; } /** Is the node the root of a build method? */ public boolean isMethodRoot() { return fMethodRoot; } /** Should the children be created in a chained method? */ public boolean useChainedChildrenMethods() { return fChainedChildrenMethods; } /** Get string description for debugging. */ public String toString() { return JavaLang.simpleClassName(fNode) + " method=" + fMethodRoot + " chained=" + fChainedChildrenMethods + " cost=" + fCreateCost + " parentCost=" + fParentCreateCost; } } /** * Constructor. */ public BuildMethodMappings(int maxCreateCostPerBuildMethod, Document document) { fDocument = document; fMaxCreateCostPerBuildMethod = maxCreateCostPerBuildMethod; calculateTreeCosts(document); } /** * Policy control: Determine if a node can be the root of a build method. * Change the logic here maybe useful in getting better packing. */ boolean canBeMethodRoot(Node node) { //FIXME: revisit switch (node.getNodeType()) { case Node.DOCUMENT_NODE: case Node.DOCUMENT_TYPE_NODE: case Node.ELEMENT_NODE: return true; default: return false; } } /** * Policy control: Get the cost of creating a single node based on its * type. This does not provide cumulative costs, its based on type only. */ public int getNodeTypeCost(Node node) { //FIXME: need costs for DocType, maybe attr if (node instanceof Element) { return CREATE_ELEMENT_CREATE_COST; } else { return CREATE_NODE_CREATE_COST; } } /** * Get a record. */ private Record getRecord(Node node) { Record rec = (Record)fNodeRecordTable.get(node); if (rec == null) { throw new XMLCError("BUG: record not found: " + node); } return rec; } /** * Traverse the tree, calculating costs and determining where build methods * should be created. Depth-first traversal, build records from the * bottom-up. This builds the table of node records. * @return The node record. */ private Record calculateTreeCosts(Node node) { Record rec = new Record(node); if (node instanceof Document) { rec.makeMethodRoot(); } // depth-fist traversal, summing cost of contained nodes ContainedNodeEnum nodes = new ContainedNodeEnum(rec.fNode); while (nodes.hasMoreElements()) { Record child = calculateTreeCosts(nodes.nextNode()); rec.adjustCreateCost(child.getParentCreateCost()); } adjustRecord(rec); return rec; } /** * Convert a child to being build in another method, adjust the size of * the parent. */ private void moveChildIntoMethod(Record parent, Record child) { parent.adjustCreateCost(-child.getParentCreateCost()); child.makeMethodRoot(); parent.adjustCreateCost(child.getParentCreateCost()); } /** * Convert a child to being build in another method, if this will reduce * the cost of building the parent. Adjust the size of the parent. */ private void minimizeChildCreateCost(Record parent, Record child) { if (!child.isMethodRoot() && child.canBeMethodRoot() && (child.getParentCreateCost() > BUILD_METHOD_CALL_CREATE_COST)) { moveChildIntoMethod(parent, child); } } /** * Build a descending sorted list of children to potentially move to their * own build methods, based to their parent create cost. */ private ArrayList buildSortedBuildMethodCandidates(Record rec) { ArrayList list = new ArrayList(); ContainedNodeEnum children = new ContainedNodeEnum(rec.fNode); while (children.hasMoreElements()) { Record child = getRecord(children.nextNode()); if (child.canBeMethodRoot()) { list.add(child); } } Comparator cmp = new Comparator() { public int compare(Object o1, Object o2) { int cost1 = ((Record)o1).getParentCreateCost(); int cost2 = ((Record)o2).getParentCreateCost(); return ((cost1 > cost2) ? -1 : ((cost1 < cost2) ? 1 : 0)); } }; Collections.sort(list, cmp); return list; } /** * Move children into other build methods until the cost of this method * is below the maximum. Sorting to move largest first results in fewer * methods being created. */ private void adjustBuildMethod(Record rec) { ArrayList list = buildSortedBuildMethodCandidates(rec); // Move largest-first into their own build methods until this method // is no longer full. int numChildren = list.size(); for (int idx = 0; (idx < numChildren) && (rec.getCreateCost() >= fMaxCreateCostPerBuildMethod); idx++) { minimizeChildCreateCost(rec, (Record)list.get(idx)); } } /** * Adjust an node record, if nececssary, to keep it from overflowing * the maximum build method create-cost. This must be called *before* the * parent record is created. */ private void adjustRecord(Record rec) { if (rec.getCreateCost() >= fMaxCreateCostPerBuildMethod) { // Split children into other build methods until this one fits adjustBuildMethod(rec); } if (rec.getCreateCost() > fMaxCreateCostPerBuildMethod) { // Not enough, create a chained method. rec.makeChainedChildrenMethods(); } } /** * Should a new method be created at the root of this node. */ public boolean isMethodRoot(Node node) { return getRecord(node).isMethodRoot(); } /** * Should the children be created in a chained method? */ public boolean useChainedChildrenMethods(Node node) { return getRecord(node).useChainedChildrenMethods(); } /** * Get the create cost of a node in it's method. */ public int getCreateCost(Node node) { return getRecord(node).getCreateCost(); } /** * Get a string repersentation of an entry for debugging. */ public String toString(Node node) { return getRecord(node).toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy