Webber: A Website Construction Application


HtmlReader.java


// A class which creates and processes a list of HtmlReader.Tag objects from HTML text

package library.html;

import java.awt.*;
import java.util.*;

import library.*;

public abstract class HtmlReader {
	private boolean doHtml;					// True for HTML output
	private LinkList tagList, tagContext;			// Tag object lists
	protected boolean html, head, body, title;		// HTML page flags

	// Constructors
	protected HtmlReader() { this(null); }
	protected HtmlReader(char[] text) { this(text, true); }
	protected HtmlReader(char[] text, boolean doHtml) {
		tagId = getTagIdList();				// Create tag ID list
		tagContext = new LinkList();			// Create tag context
		setText(text); 					// Initialize tag list
		this.doHtml = doHtml;				// Set output-mode flag
	}

	// Abstract methods for obtaining tag classes
	protected abstract Class[] getTagClasses();
	protected abstract Class getDefaultTagClass();
	
	// Create an instance of an HtmlReader.Tag class (trap exceptions)
	private static Tag newTagClass(Class c) {
		try { return (Tag)c.newInstance(); }
		catch (Exception e) {}
		return null;	// Failed
	}

	// Create a tag of a type i
	private Tag newTag(int i) { return newTagClass(getTagClasses()[i]); }

	// Create a default tag type
	private Tag defaultTag() { return newTagClass(getDefaultTagClass()); }

	// Return the length of the tag/block at pos in text (or 1 on error)
	private static int blockLength(char[] text, int pos) {
		char c = '>';
		int x = 0;
		if (text[pos]=='<') ++x; else c = '<';
		return ((x+=Lib.nextChar(text, pos, c)) > text.length) ? 1 : x-pos;
	}

	// Create and return a list of all available tag ID strings
	private LinkList tagId;					// Tag ID list
	private LinkList getTagIdList() {
		LinkList list = new LinkList();			// Create tag ID list
		for (int i=0, num = getTagClasses().length; i<num; ++i)
			list.addElement(newTag(i).getTagId());	// Add tag ID to list
		return list;
	}

	// Return the tag ID at pos in text (or null)
	private static String getTagId(char[] text, int pos) {
		int i;
		if (text[pos]!='<') return null;		// Not a tag?
		if (++pos < text.length) if (text[pos]=='/') ++pos;
		for (i=pos; i<text.length; ++i)
			if (!Character.isLetter(text[i])) break;
		return (i<text.length) ? new String(text, pos, i-pos) : null;
	}		

	// Return the tag class index for the tag at pos in text (or -1)
	private int getTagIndex(char[] text, int pos) {
		String s, id;
		if ((s = getTagId(text, pos))==null) return -1;	// Check tag validity
		if (Lib.nextChar(text, pos, '>')==text.length) return -1;
		Enumeration e = tagId.elements();		// Get list elements
		for (int i=0; e.hasMoreElements(); ++i) {
			id = (String) e.nextElement();		// Get next tag ID
			if (s.equalsIgnoreCase(id)) return i;	// Matching tag ID?
		}
		return -1;					// Unrecognized tag
	}

	// Identify and return a HtmlReader.Tag for tag at pos in text
	private Tag getTagType(char[] text, int pos) {
		int length = blockLength(text, pos), i;
		if ((i = getTagIndex(text, pos))>=0)		// Get tag index
			return newTag(i).init(text, pos, length);
		return defaultTag().init(text, pos, length);	// Text/unknown tag
	}

	// Create a tag-list from text
	private LinkList getTagList(char[] text) {
		LinkList list = new LinkList();			// Create a tag list
		if (text!=null) for (int i=0; i<text.length; ) {
			Tag tag = getTagType(text, i);
			list.addElement(tag);			// Add tag to list
			i += tag.getLength();			// Advance past tag
		}
		return list;					// Return tag list
	}

	// Create the complete tag-list from text
	public synchronized void setText(char[] text) { tagList = getTagList(text); }

	// Create a tag-list containing a single entry for a block of plain text
	public synchronized void setPlainText(char[] text) {
		tagList = new LinkList();			// Create a new list
		tagList.addElement(defaultTag().init(text, 0, text.length));
	}

	// Return the HtmlReader.Tag at the specified index in tagList
	private Tag tagAt(int index) { return (Tag)tagList.elementAt(index); }

	// Return the tagList index of the tag containing pos
	private int getTagIndex(int pos) {
		Tag t;
		Enumeration e = tagList.elements();		// Get tag elements
		for (int i=0; e.hasMoreElements(); ++i) {	// Check tags for pos
			t = (Tag)e.nextElement();		// Get next tag
			if (pos < (t.getPos() + t.getLength())) return i;
		}
		return -1;					// Not found - append
	}

	// Update the tag list after adding or removing offset chars at pos in text
	public synchronized void updateText(char[] text, int pos, int offset) {
		int i, si, ei, size = tagList.size();		// Get tag list size
		if (size < 1) { setText(text); return; }	// No current text?
		if ((i = getTagIndex(pos)) < 0) i = size - 1;	// Find tag at pos
		si = (i > 0 ) ? i - 1 : i;			// Include prior tag?
		ei = ((i + 1) < size) ? i + 1 : i;		// Include next tag?
		Tag st = tagAt(si), et = tagAt(ei);		// Start/end tags
		pos = st.getPos();				// Get text start
		i = et.getPos() + et.getLength() - pos;		// Original text length
		String s = new String(text, pos, i+offset);	// Get modified range
		tagList.removeLinksAt(si, ei - si + 1);		// Remove old tags
		LinkList list = getTagList(s.toCharArray());	// Create new tags
		tagList.addLinksAt(list, si);			// Insert new tags
		updateTags(text);				// Update tag data
	}

	// Update all tags to refer to new text (get absolute positions from deltas)
	private void updateTags(char[] text) {
		Enumeration e = tagList.elements();		// Get list elements
		int pos = 0;					// New pos counter
		Tag tag;
		while (e.hasMoreElements()) {			// Modify all tags
			tag = (Tag)e.nextElement();		// Get next tag
			tag.setText(text);			// Set text reference
			tag.setPos(pos);			// Set new text pos
			pos += tag.getLength();			// Move to next tag
		}
	}

	// Call a Tag output method to produce either HTML or text output
	private boolean doTag(Tag t, HtmlPage p, Graphics g) {
		return doHtml ? t.action(p, g) : t.drawTag(p, g);
	}

	// Draw tags in tagList on HtmlPage p
	public void drawText(HtmlPage p, Graphics g) {
		html = head = body = title = false;		// Initialize page flags
		Enumeration e = tagList.elements();		// Get list elements
		Tag t;
		while (e.hasMoreElements()) {			// Run to end of page
			t = (Tag)e.nextElement();		// Get the next tag
			if (!doTag(t, p, g)) break;		// Draw the tag
		}
		tagContext.removeAllElements();			// Reset tag-context list
	}

	// Set the HTML output-mode flag (true = HTML, false = text)
	public void setHtmlEnabled(boolean state) { doHtml = state; }


	/* The Tag Context List
	The following methods implement the list of HtmlReader.Tag references held by
	the HtmlReader.tagContext variable.  This list is used by HtmlReader.Tag objects
	(such as those implementing lists) during the processing of a HTML page to store
	references to themselves which can be accessed later in the processing phase by
	other HtmlReader.Tag objects (such as list items) to obtain context-sensitive
	data (such as list-depth).
	*/	

	// Get the index of the active (last) instance of the specified context
	private int getContextIndex(String id) {
		Enumeration e = tagContext.elements();		// Get context list
		int i, x = -1;
		for (i=0; e.hasMoreElements(); ++i) {		// Pass through list
			Context c = (Context)e.nextElement();	// Get next context
			if (id.equals(c.getContextId())) x = i;	// Compare ID strings
		}
		return x;					// Last index (or -1)
	}

	// Append a tag context to the context list
	public void addContext(Context t) { tagContext.addElement(t); }

	// Remove the active (last) instance of the specified context
	public void removeContext(String id) {
		tagContext.removeElementAt(getContextIndex(id));
	}

	// Return the active (last) instance of the specified context (or null)
	public Context getContext(String id) {
		return (Context)tagContext.elementAt(getContextIndex(id));
	}


// Inner-interface used by a HtmlReader to access custom tag classes
public interface Tag {

	// Initialize tag
	public Tag init(char[] text, int pos, int length);

	// Get tag ID string
	public String getTagId();

	// Get tag length
	public int getLength();

	// Get tag position
	public int getPos();

	// Set tag position
	public void setPos(int pos);

	// Set tag text reference
	public void setText(char[] text);

	// Returns true if the tag is a default/text tag
	public boolean isTextTag();

	// Draw the actual text represented by the tag
	public boolean drawTag(HtmlPage p, Graphics g);	// False if end of page

	// Perform the function of the tag
	public boolean action(HtmlPage p, Graphics g);	// False if end of page
}


// Inner-interface implemented by HtmlReader.Tag objects that need to create a temporary
// 'page context' (eg. lists) by attaching themselves to the HtmlReader.  This allows a
// HtmlReader.Tag to store data at the HtmlReader level.
public interface Context {

	// Returns a string used to identify the context type
	public String getContextId();
}
}

Go To: Source Code