📄 tag.java
字号:
package com.gameislive.browser;
import java.util.*;
/**
* this class represents xml tags. they should
* have a name and a state, and a hash of parms
* if applicable. (currently a string...)
*
* waplet classes are not currently in a package, because
* of strange browser/jar behavior with packages.
*
* @author kxml
*/
public class Tag{
/** tag state for an undefined tag */
public static final int UNDEFINED = 0;
/** tag state for an open tag */
public static final int TAG_OPEN = 1;
/** tag state for a close tag (starts with /) */
public static final int TAG_CLOSE = 2;
/** tag state for a self-contained tag (ends with /) */
public static final int TAG_SELFCONTAINED = 3;
/** stores the text tag passed in */
private String _tagtext;
/** the tag name, or primary identifier */
private String _name;
/** a hash of parameters for this tag */
private Hashtable _parms;
/** an internal field representing the tag state */
private int _state;
/** constructor creates an empty tag with undefined state. */
public Tag(){
_name = "";
_parms = null;
_state = UNDEFINED;
}
/** constructor parses a text representation. */
public Tag(String text){
this();
_tagtext = text;
if(!(text.charAt(0) == '<' && text.charAt(text.length()-1) == '>')){
_name = text;
}
else{
// can't lowercase it here: if a parameter is a url, it
// needs to maintain case.
// text = text.substring(1, text.length() - 1).toLowerCase();
// clear out opening and closing brackets
text = text.substring(1, text.length() - 1);
// check the state
if(text.charAt(0) == '/'){
text = text.substring(1).trim();
_state = TAG_CLOSE;
}
else if(text.charAt(text.length()-1) == '/'){
text = text.substring(0, text.length() - 1).trim();
_state = TAG_SELFCONTAINED;
}
else{
text = text.trim();
_state = TAG_OPEN;
}
// check for parameters, if any: parse them.
int idx = 0;
if((idx = text.indexOf(' ')) > -1){
parseParms( text.substring(idx + 1).trim());
// now that you have the parms, lower case the id...
text = text.substring(0, idx).trim().toLowerCase();
}
_name = text;
}
}
/**
* the string is chopped into name value pairs, and pairs
* are stored in the parameter hash. watch out for quoted values:
* don't put the quotes in the hash. there shouldn't be
* any space between the key and the value, each pair should
* look like key=value.
*/
public void parseParms( String s){
_parms = new Hashtable();
String key = null;
String val = null;
int idx = 0;
int eidx = 0;
while((idx = s.indexOf('=')) > -1){
key = s.substring(0, idx).trim();
s = s.substring(idx + 1).trim();
// is s quoted? if so, look for the end quote...
if(s.charAt(0) == '"')
val = s.substring(1, (eidx = s.indexOf('"', 1)));
else {
eidx = s.indexOf( ' ');
if( eidx == -1) eidx = s.length() - 1;
val = s.substring(0, eidx);
}
s = s.substring(eidx + 1).trim();
_parms.put(key, val);
}
}
/** accessor method for tag id */
public String getName(){ return _name; }
/** set the id of a tag */
public void setName(String name){ this._name = name; }
/** accessor method for parameter hash */
public Hashtable getParms(){ return _parms; }
/** set the parameter hash directly */
public void setParms(Hashtable parms){ this._parms = parms; }
/** accessor method for a single parameter value, by key (name). */
public String getParm(String key){
Object obj = null;
if(null == _parms) return null;
return (null == (obj = _parms.get(key))) ? null : (String)obj;
}
/**
* set a single parameter name/value pair. this method will
* overwrite existing values.
*/
public void setParm(String key, String val){
if(null == _parms) _parms = new Hashtable();
_parms.put(key, val);
}
/** accessor method for the tag state. */
public int getState(){ return _state; }
/** set the tag state directly. */
public void setState(int state){ this._state = state; }
/**
* return a string representation of the tag. this includes the
* name and all name-value parameter pairs. (this is a descriptor
* method, it won't return the actual tag that was passed in).
*/
public String toString(){
StringBuffer sb =
new StringBuffer("Tag name=<" + _name + "> state = " + _state);
if(null != _parms){
String key = null;
for(Enumeration e = _parms.keys(); e.hasMoreElements(); ){
key = ((String)(e.nextElement()));
sb.append("\n\t" + key + "=[" +
((String)(_parms.get(key))) + "]");
}
}
return sb.toString();
}
/**
* this method return a tag,if parm is undefined,then return null
*/
public static Tag MakeTag(Object o){
Tag t = new Tag((String)o);
return (t.getState() == Tag.UNDEFINED) ? null : t;
}
/**
* this method builds the document tree. it'll throw an
* exception on a document error, which is handy for
* validating page code. it should probably display a
* specific error screen, but for now it just dumps to
* std out.
*
* store the tree: put it all in a vector. don't worry
* about level indices, because it'll fail here if it's bad.
* start text with a ' (').
*
* catch exceptions, for a better stack trace, but then
* throw them back.
*/
public static Vector Tree( Vector v) throws Exception{
// maintain a hash, keyed by 'level', with values
// of the tag names. fail on a bad match.
Hashtable tree = new Hashtable();
Vector document = new Vector();
// temporary tag, string ref, object holder
Tag tag = null;
String tmp = null;
Object obj = null;
// current level
int currentLevel = -1;
boolean ignore = false;
for(Enumeration e = v.elements(); e.hasMoreElements(); ){
tmp = ( null == ( obj = e.nextElement())) ? "" : (String)obj;
//if( null == obj) System.out.println("null element!");
//System.out.println("el: " + tmp);
if(tmp.startsWith("<!--")){
ignore = true;
}
if(ignore){
if(tmp.endsWith("-->")){
ignore = false;
}
System.out.println("IGNORE: "+tmp);
continue;
}
if(null != (tag = MakeTag(tmp))){
if(tag.getState() == Tag.TAG_OPEN){
currentLevel++;
tree.put(new Integer(currentLevel), tag);
document.addElement(tag);
}
else if(tag.getState() == Tag.TAG_CLOSE){
if( null == tree.get( new Integer( currentLevel))){
String xmessage =
" attempted close of </" + tag.getName() +
"> at root level";
throw new Exception( xmessage);
}
else if(tag.getName().equals(((Tag)(tree.get(
new Integer(currentLevel)))).getName())){
currentLevel--;
document.addElement(tag);
}
else{
String xmessage =
" attempted close of </" + tag.getName() +
"> in body of <" + ((Tag)(tree.get(
new Integer(currentLevel)))).getName() + ">";
throw new Exception( xmessage);
}
}
else if(tag.getState() == Tag.TAG_SELFCONTAINED){
document.addElement(tag);
}
}
// it's text...
else document.addElement(new String(CleanString(tmp)));
}
return document;
}
/**
* this method handles escaped chars in the string, and
* also the double $ substitutions. the renderer just takes
* plain text.
*/
private static String CleanString(String s){
// first, translate $ to $...
int idx = 0;
while((idx = s.indexOf( "$")) > -1){
s = s.substring(0, idx) + s.substring(idx+1);
}
StringBuffer sb = new StringBuffer("");
StringBuffer character = new StringBuffer("");
int len = s.length();
idx = 0;
char c = ' ';
boolean esc = false;
// loop keys on & and ; to parse escaped characters.
// it then does a lookup to swap the strings.
while(idx < len){
c = s.charAt(idx);
if( esc){
character.append(c);
if( c == ';'){
String tmp = character.toString();
if(tmp.startsWith("&#x")){
tmp = Tools.Convert(tmp);
}
tmp = Tools.ReplaceTagString(tmp);
sb.append(tmp);
//obj = escapeCharacters.get( tmp);
//if( null != obj) sb.append((String)obj);
esc = false;
}
}
else if( c == '&'){
character = new StringBuffer();
character.append(c);
esc = true;
}
else{
sb.append(c);
}
idx++;
}
return sb.toString();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -