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