| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package adobe.abc; |
| |
| import java.io.PrintWriter; |
| import java.util.Stack; |
| |
| /** |
| * The TraceManager exposes a trace-oriented API |
| * that writes entries in XML format. |
| * @note Doesn't use XMLWriter so that the trace |
| * entries can be manually indented; the trace |
| * files get very large and difficult to read |
| * using a browser or other ready-to-hand viewer. |
| * @author Tom Harwood |
| * |
| */ |
| public class TraceManager |
| { |
| private Stack<TraceElement> activeElements; |
| |
| // Track number of elements in the stack to indent |
| // the trace file, to highlight its structure. |
| private StringBuffer indentBuffer = new StringBuffer(); |
| |
| private PrintWriter sink; |
| |
| private boolean enabled = true; |
| |
| public TraceManager() |
| { |
| activeElements = new Stack<TraceElement>(); |
| } |
| |
| public void enable(PrintWriter sink) |
| { |
| this.sink = sink; |
| } |
| |
| private boolean isEnabled() |
| { |
| return enabled && sink != null; |
| } |
| |
| public void disable() |
| { |
| if ( isEnabled() && activeElements.size() > 0 ) |
| finishCurrentElement(); |
| enabled = false; |
| } |
| |
| public void enable() |
| { |
| enabled = true; |
| } |
| |
| public int pushPhase(String phaseName) |
| { |
| return pushElement(phaseName, false); |
| } |
| |
| /** |
| * @param elementName -- the name of the new trace element. |
| * Written as the name of the trace's XML tag. |
| * @param popOnNext -- when set, this is a one-line trace entry. |
| * When not set, this is a trace phase that may have |
| * sub-entries. |
| * @return a trace element stack mark, used to unwind the trace |
| * when this trace phase finishes. |
| * @see unwind() |
| */ |
| public int pushElement(String elementName, boolean popOnNext) |
| { |
| if ( 0 == activeElements.size() ) |
| println("<?xml version='1.0' encoding='utf-8' ?>"); |
| else |
| finishCurrentElement(); |
| |
| // Result must be computed after finishCurrent() |
| // b/c finishCurrent() may change the stack size. |
| int result = activeElements.size(); |
| activeElements.push(new TraceElement(elementName, popOnNext)); |
| indentBuffer.append(' '); |
| |
| StringBuffer output = getIndentedBuffer(); |
| output.append("<"); |
| output.append(elementName); |
| |
| print(output.toString()); |
| |
| return result; |
| } |
| |
| TraceElement popElement() |
| { |
| TraceElement finished_element = activeElements.pop(); |
| |
| if ( isEnabled() ) |
| { |
| if ( finished_element.hasBody ) |
| { |
| StringBuffer output = getIndentedBuffer(); |
| output.append("</"); |
| output.append(finished_element.phaseName); |
| output.append(">"); |
| println(output.toString()); |
| } |
| else |
| { |
| println("/>"); |
| } |
| |
| sink.flush(); |
| } |
| indentBuffer.deleteCharAt(0); |
| return finished_element; |
| } |
| |
| private StringBuffer getIndentedBuffer() |
| { |
| return new StringBuffer(indentBuffer); |
| } |
| |
| private void finishCurrentElement() |
| { |
| if ( activeElements.peek().popOnNext ) |
| { |
| popElement(); |
| } |
| else if ( ! activeElements.peek().hasBody ) |
| { |
| println(">"); |
| activeElements.peek().setHasBody(true); |
| } |
| } |
| |
| public void unwind(int mark) |
| { |
| while ( activeElements.size() > mark) |
| popElement(); |
| } |
| |
| public void traceEntry(String tagName) |
| { |
| pushElement(tagName, true); |
| } |
| public void traceEntry(String tagName, String attrName, String attrValue) |
| { |
| traceEntry(tagName); |
| addAttr(attrName, attrValue); |
| } |
| |
| public void traceEntry(String tagName, String attrValue) |
| { |
| traceEntry(tagName, "value", attrValue); |
| } |
| |
| public void traceEntry(String tagName, int attrValue) |
| { |
| traceEntry(tagName, Integer.toHexString(attrValue)); |
| } |
| |
| public void addAttr(String attrName, String attrValue) |
| { |
| if ( isEnabled() ) |
| { |
| String xml_escaped_value = attrValue.replaceAll("&", "&"); |
| xml_escaped_value = xml_escaped_value.replaceAll("<", "<"); |
| // Should be done, but it makes the -> edge descriptions hard to read |
| xml_escaped_value = xml_escaped_value.replaceAll(">", ">"); |
| xml_escaped_value = xml_escaped_value.replaceAll("\"", """); |
| xml_escaped_value = xml_escaped_value.replaceAll("\r", "\\r"); |
| print(" " + attrName + "=\"" + xml_escaped_value + "\""); |
| } |
| } |
| |
| public void addAttr(String attrName, int attrValue) |
| { |
| print(" " + attrName + "=\"" + attrValue + "\""); |
| } |
| |
| public void addAttr(String attrName, Object attr) |
| { |
| if ( isEnabled() ) |
| { |
| String attrValue; |
| |
| if ( attr != null ) |
| { |
| attrValue = attr.toString(); |
| if ( attrValue.replaceAll("\\s", "").equals("null") ) |
| { |
| attrValue = "0x" + Integer.toHexString(attr.hashCode()); |
| } |
| } |
| else |
| { |
| attrValue = "null"; |
| } |
| |
| addAttr(attrName, attrValue); |
| } |
| } |
| |
| void println(String s) |
| { |
| if ( isEnabled() ) |
| sink.println(s); |
| } |
| |
| void print(String s) |
| { |
| if ( isEnabled()) |
| sink.print(s); |
| } |
| |
| void flush() |
| { |
| if ( isEnabled() ) |
| sink.flush(); |
| } |
| |
| private class TraceElement |
| { |
| String phaseName; |
| boolean hasBody = false; |
| boolean popOnNext = false; |
| |
| TraceElement(String phaseName) |
| { |
| this.phaseName = phaseName; |
| } |
| |
| TraceElement(String entryName, boolean popOnNext) |
| { |
| this.phaseName = entryName; |
| this.popOnNext = popOnNext; |
| } |
| |
| void setHasBody(boolean has_body) |
| { |
| this.hasBody = has_body; |
| } |
| } |
| } |