Package net.xoetrope.awt

Source Code of net.xoetrope.awt.XMetaContent

package net.xoetrope.awt;

import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;
import java.io.StringReader;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.ScrollPane;

import net.xoetrope.xml.XmlElement;
import net.xoetrope.xui.XMetaContentHolder;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.style.XStyle;
import net.xoetrope.xui.style.XStyleComponent;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.helper.XTranslator;


/**
* <p>This component renders XML based content. A simple set of tags
* for layout and formatting are used. Styles can be used to control attributes
* such as fonts and colors</p>
* <p>For example the following taken from the Xui Zoo sample application shows some formatted paragraphs of text:</p>
* <pre>
* &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;XText&gt; &lt;XStyle name=&quot;Heading&quot;&gt;Congratulations!&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;You have just been appointed general manager of the XUI Zoo.&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Unfortunately your predescessor wasted most of his time struggling with his Java code. To keep the zoo in business you must buy some new animals quickly. You need to buy 100  animals, including Monkies, Elephants and to get new visitors to the zoo you need to have at least one amazing Unicorn.&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Monkies are common enough and only cost 0.05 $XML, Elephants on the other hand are big animals and cost a full 1.00 $XML. And the most rare Unicorns cost the princely sum of  5.00 $XML&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Please fill in your order quantities below.&lt;/XStyle&gt; &lt;/XText&gt;
* </pre>
* <p>The tag supported by this class are listed below and play a similar role to their html equivalents:
* <UL>
* </LI>
* <LI>XSTYLE - sets the style for the text.
* <LI>BR - A break.
* <LI>UL - A unordered list.
* <LI>LI - A list item.
* <LI>TABLE - A table.
* <LI>TD - A table cell declaration.
* <LI>TR - A table record/row declaration.
* </p>
* <p>The tags can be nested as you would do whith HTML.</p>
* <p>Copyright (c) Xoetrope Ltd., 1998-2003<br>
* License:      see license.txt
* @version $Revision: 2.10 $
*/
public class XMetaContent extends Component implements XMetaContentHolder, XStyleComponent
{
  private Stack styleStack;
  private XmlElement content;
  private String strContent;
  private String source;

  private int currentX, currentY;
  private int width, height;

  private int startX, colStart;

  private XStyle currentStyle;
  private String currentStyleName;
  private Font font;
  private FontMetrics fontMetrics;
  private int fontHeight;
  private int contentHeight;
  private int padding;

  protected XTranslator translator;

  /**
   * A table of the method names needed for rendereing each style
   */
  protected static Hashtable methodTable = null;

  private static final int XSTYLE = 1;
  private static final int BR = 2;
  private static final int UL = 3;
  private static final int LI = 4;
  private static final int TABLE = 5;
  private static final int TR = 6;
  private static final int TD = 7;
 
  private XProject currentProject;

  /**
   * Constructs anew XMetaContent component
   */
  public XMetaContent()
  {
    styleStack = new Stack();
    currentStyle = new XStyle();
    currentStyleName = "";

    currentProject = XProjectManager.getCurrentProject();
    translator = currentProject.getTranslator();

    fillMethodTable();
  }

  /**
   * Initialises the lookup hash table of handlers for the content. The hash table
   * provides a quick way of looking up the tag names for subsequent use in a
   * switch.
   */
  protected void fillMethodTable()
  {
    if ( methodTable == null ) {
      methodTable = new Hashtable();
      methodTable.put( "xstyle", new Integer( XSTYLE ));
      methodTable.put( "br", new Integer( BR ));
      methodTable.put( "ul", new Integer( UL ));
      methodTable.put( "li", new Integer( LI ));
      methodTable.put( "table", new Integer( TABLE ));
      methodTable.put( "tr", new Integer( TR ));
      methodTable.put( "td", new Integer( TD ));
    }
  }

  /**
   * Set the meta content to the content of an xml file
   * @param fileName the new content
   */
  public void setFileName( String fileName )
  {
    try {
      if ( fileName != null ) {
        XmlElement src = XmlSource.read( currentProject.getBufferedReader( fileName, null ) );
        setContent( fileName, src );
      }
    }
    catch ( Exception ex ) {
      DebugLogger.logWarning( "Unable to load content: " + fileName );
      DebugLogger.logWarning( "Please check the case of the file name" );
      ex.printStackTrace();
    }
  }

  /**
   * Set the meta content.
   * @param xmlSrc the location from which the xml was obtained
   * @param src the input xml
   */
  public void setContent( String xmlSrc, XmlElement src )
  {
    if ( xmlSrc.indexOf( "<?xml" ) == 0 )
      strContent = xmlSrc;
    else if ( xmlSrc.indexOf( ".xml" ) > 0 ) {
      try {
        src = XmlSource.read( currentProject.getBufferedReader( xmlSrc, null ) );
      }
      catch ( Exception ex ) {
        DebugLogger.logWarning( "Unable to load content: " + xmlSrc );
        DebugLogger.logWarning( "Please check the case of the file name" );
        ex.printStackTrace();
      }
    }
    source = xmlSrc;
    content = src;
    contentHeight = 0;
    padding = 2;
    calcSize();
    repaint();
  }

  /**
   * Set the meta content to a simple string value
   * @param newContent the new content to display
   */
  public void setContent( String newContent )
  {
    if (( strContent != null ) && ( strContent.indexOf( "<?xml" ) == 0 )) {
      XmlElement src = XmlSource.read( new StringReader( newContent ) );
      setContent( newContent, src );
    }
    else if ( newContent.indexOf ( ".xml" ) > 0 )
      setContent( newContent, null );
    else {
      strContent = newContent;
      content = null;
      source = null;
      contentHeight = 0;
      padding = 2;
      calcSize();
      repaint();
    }
  }

  /**
   * Set the current style.
   * @param style the style name
   */
  public void setStyle( String style )
  {
    currentStyle = XProjectManager.getStyleManager().getStyle( style );
  }

  /**
   * Get the meta content's name .
   * @return the model name of the content
   */
  public String getContent()
  {
    if ( source!=null )
      return source;
    else
      return strContent;
  }

  /**
   * Get the source of the current content
   * @return the raw content
   */
  public String getSource()
  {
    return source;
  }

  /**
   * Renders the component
   * @param g the graphics context
   */
  public void paint( Graphics g )
  {
    update( g );
  }

  /**
   * Performs the actual rendering
   * @param g the graphics content
   */
  public void update( Graphics g )
  {
    currentX = currentY = startX = padding;
    height = getSize().height - 2 * padding;

    g.setColor( getBackground() );
    g.fillRect( 0, 0, getSize().width, getSize().height );

    try {
        if (( content != null ) && ( content.getName().compareTo( "XText" ) == 0 )) {
          font = g.getFont();
          if ( font.getSize() == 0 )
           font = new Font( null, 0, 12 );
          g.setFont( font );

          if ( contentHeight == 0 )
            calcSize();
          else
            render( g, content );
        }
        else if (( content == null ) && ( strContent != null )) {
          applyStyle( g );
          renderText( g, strContent );
        }
    }
    catch ( Exception e )
    {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "Exception while rendering metacontent: " + getContent());
    }

    int renderHeight = currentY + fontHeight + 2 * padding;
    if ( renderHeight != contentHeight ) {
      Component parent = getParent().getParent();
      contentHeight = renderHeight;
      if ( parent instanceof ScrollPane ) {
        setSize( getParent().getSize().width - 12, contentHeight );
        parent.doLayout();
      }
      else
        repaint();
    }
  }

  /**
   * Get the preferred display size for the component
   * @return the preferred size
   */
  public Dimension getPreferredSize()
  {
    return new Dimension( getSize().width, contentHeight );
  }

  /**
   *  Sets the padding or indent for the content
   * @param pad the amount of padding with which to surround the content
   */
  public void setPadding( int pad )
  {
    padding = pad;
  }

  /**
   * Calculate the size of the content. This method is called from within the
   * paint method and recalculates the required size for display of the content.
   * If a scrollpane is the parent then this control is resized so that all the
   * content  will be visible. The scrollpane may initially have no scrollbar so
   * to avoid flicker and multiple repaints as the control is sized and offscreen
   * graphics context is used for the sizing.
   */
  private void calcSize()
  {
    width = getSize().width;
    if ( contentHeight == 0 )
      width -= 20;
    contentHeight = -1;
    java.awt.Image offscreen = createImage( 1, 1 );

    if ( offscreen != null ){
      Graphics og = offscreen.getGraphics();
      og.setClip( 0, 0, width, height );
      paint( og );
      og.dispose();
    }
  }

  /**
   * Renders the content, recursively descending the XML
   * @param g the graphics context
   * @param element the root element
   */
  private void render( Graphics g, XmlElement element )
  {
    applyStyle( g );

    Vector elements = element.getChildren();
    int numElements = elements.size();

    for ( int i = 0; i < numElements; i++ ) {
      XmlElement child = (XmlElement)elements.elementAt( i );
      int method = ((Integer)methodTable.get( child.getName().toLowerCase())).intValue();

      if ( !renderItem( g, child, method ))
        renderText( g, child.getContent() );

      pushStyle();
      render( g, child );
      popStyle();
    }
  }

  /**
   * Renders an individual item/tag
   * @param g the graphics context
   * @param child the xml element being rendered
   * @param method the element name
   * @return true if the type is found
   */
  protected boolean renderItem( Graphics g, XmlElement child, int method )
  {
    switch ( method ) {
      case XSTYLE:
        setStyle( g, child.getContent(), child.getAttribute( "name" ) );
        break;
      case BR:
        lineBreak( g );
        break;
      case UL:
        renderList( g );
        break;
      case LI:
        renderListItem( g, child.getContent() );
        break;
      case TABLE:
        renderTable( g );
        break;
      case TR:
        renderTableRecord( g );
        break;
      case TD:
        renderTableCell( g, child.getContent(), child.getAttribute( "width" ));
        break;
      default:
        return false;
    }

    return true;
  }

  /**
   * Changes the current style. The style element may have content and it is
   * assumed to be text when rendering.
   * @param g the graphics context
   * @param content the current xml node
   * @param styleName the style name
   */
  private void setStyle( Graphics g, String content, String styleName )
  {
    currentX = startX = padding;
    if ( styleName.compareTo( currentStyleName ) != 0 ) {
      currentStyle = XProjectManager.getStyleManager().getStyle( styleName );
      currentStyleName = styleName;

      applyStyle( g );
    }

    renderText( g, content );
  }

  /**
   * Applies the current style to the graphics context
   * @param g the graphics context
   */
  private void applyStyle( Graphics g )
  {
    g.setColor( currentStyle.getStyleAsColor( XStyle.COLOR_FORE ));

    try {
      font = XProjectManager.getStyleManager().getFont( currentStyle );
    }
    catch ( Exception ex ) {
      font = new Font("Arial", 0, 12);
    }
    if ( font!=null )
      g.setFont( font );
    fontMetrics = g.getFontMetrics();
    fontHeight = fontMetrics.getHeight();
  }

  /**
   * Pushes the style onto the style stack. Called prior to descending a level
   * in the xml
   */
  private void pushStyle()
  {
    styleStack.push( currentStyle.clone());
  }

  /**
   * Pops the style from the style stack. Called on return from a recursive call
   * to render a lower level of the xml hierarchy
   */
  private void popStyle()
  {
    currentStyle = (XStyle)styleStack.pop();
  }

  /**
   * Renders text content
   * @param g the graphcis context
   * @param text the text content
   */
  protected void renderText( Graphics g, String text )
  {
    // Draw the question text over multiple lines
    g.setColor( currentStyle.getStyleAsColor( XStyle.COLOR_BACK ));
    if ( text == null )
        return;

    int iStart = text.indexOf( "${" );
    int iEnd = text.indexOf( "}", iStart );
    while ( iStart > -1 )
    {
      String key = text.substring( iStart + 2, iEnd );
      String temp = text.substring( 0, iStart );
      temp += translator.translate( key );
      temp += text.substring( iEnd + 1, text.length() );
      text = temp;
      iStart = text.indexOf( "${" );
      iEnd = text.indexOf( "}", iStart );
    }
   
    int start = 0;
    int end = 0;
    int lineNum = 1;

    boolean newLine = false;
    do {
      boolean drawLine = false;

      // Extend the string by a word (to the next space, if any)
      end = text.indexOf( ' ', end+1 );
      String ss;
      if ( end > 0 )
        ss = text.substring( start, end );
      else {
        ss = text.substring( start );
        drawLine = true;
      }

      // Does the next word fit in?
      if (( currentX + fontMetrics.stringWidth( ss )) < width )
        ;//line = ss;
      else {
        end = start + ss.lastIndexOf( ' ' );
        if ( end >= 0 )
          ss = text.substring( start, end );
        else
          ss = text.substring( start );
        drawLine = true;
        newLine = true;
      }

      if ( drawLine ) {
        // Erase the background
        g.setColor( getBackground() );
        g.fillRect( currentX, currentY, (int)fontMetrics.stringWidth( ss ) + 1, fontHeight + 1 );

        // Draw the text
        g.setColor( getForeground());
        g.drawString( ss, currentX, currentY + fontMetrics.getAscent());

        // Update the current position.
        currentX += (int)fontMetrics.stringWidth( ss );
        start = end + 1;
        lineNum++;
        if ( newLine ) {
          currentY +=  fontHeight;
          currentX = startX;
          newLine = false;
        }
      }
    }
    while ( end >= 0 );

    // If no text drawn, then do it now
    if (( start == 0 ) && ( lineNum == 1 ))
      g.drawString( text, currentX, fontMetrics.getAscent());
  }

  /**
   * Render a list
   * @param g
   */
  private void renderList( Graphics g )
  {
    lineBreak( g );
  }

  /**
   * Render a list item
   * @param g
   * @param text
   */
  private void renderListItem( Graphics g, String text )
  {
    int oldStart = startX;
    int bulletSize = fontHeight / 4;

    currentY += fontHeight / 2;

    startX += 2 * fontHeight;
    lineBreak( g );

    g.fillOval( startX - fontHeight / 2, currentY + fontHeight / 2, bulletSize, bulletSize );

    renderText( g, text );
    startX = oldStart;
  }

  /**
   * Prepare to render a table element
   * @param g
   */
  private void renderTable( Graphics g )
  {
    startX += 2 * fontHeight;
    colStart = startX;
    lineBreak( g );
  }

  /**
   * Render a record of a table.
   * @param g
   */
  private void renderTableRecord( Graphics g )
  {
    lineBreak( g );
    startX = colStart;
    currentX = startX;
  }

  /**
   * Render an individual table cell
   * @param g
   * @param text
   * @param width
   */
  private void renderTableCell( Graphics g, String text, String width )
  {
//    int bulletSize = fontHeight / 4;

    renderText( g, text );

    startX += Integer.parseInt( width );
    currentX = startX;
  }

  /**
   * Add a line break
   * @param g
   */
  private void lineBreak( Graphics g )
  {
    currentY += fontHeight;
    currentX = startX;
  }

  /**
   * Get the calculated height of the content. Used when the content needs to be scrolled
   * @return The height in pixels
   */
  public int getContentHeight()
  {
    return contentHeight;
  }
}
TOP

Related Classes of net.xoetrope.awt.XMetaContent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.