Various minor optimizations.

This commit is contained in:
hns 2002-10-15 17:52:00 +00:00
parent c796392946
commit 063b42744c

View file

@ -21,9 +21,9 @@ import java.util.*;
public final class Skin { public final class Skin {
private Macro[] parts; private Macro[] parts;
private Application app; private final Application app;
private char[] source; private final char[] source;
private int sourceLength; private final int sourceLength;
private HashSet sandbox; private HashSet sandbox;
@ -98,20 +98,28 @@ public final class Skin {
/** /**
* Render this skin * Render this skin
*/ */
public void render (RequestEvaluator reval, Object thisObject, HashMap paramObject) throws RedirectException { public void render (RequestEvaluator reval, Object thisObject, Map paramObject) throws RedirectException {
if (parts == null) // check for endless skin recursion
if (++reval.skinDepth > 50)
throw new RuntimeException ("Recursive skin invocation suspected");
if (parts == null) {
reval.res.writeCharArray (source, 0, sourceLength); reval.res.writeCharArray (source, 0, sourceLength);
reval.skinDepth--;
return;
}
try { try {
// check for endless skin recursion
if (++reval.skinDepth > 50)
throw new RuntimeException ("Recursive skin invocation suspected");
int written = 0; int written = 0;
Map handlerCache = null;
if (parts.length > 3) {
handlerCache = new HashMap();
}
for (int i=0; i<parts.length; i++) { for (int i=0; i<parts.length; i++) {
if (parts[i].start > written) if (parts[i].start > written)
reval.res.writeCharArray (source, written, parts[i].start-written); reval.res.writeCharArray (source, written, parts[i].start-written);
parts[i].render (reval, thisObject, paramObject); parts[i].render (reval, thisObject, paramObject, handlerCache);
written = parts[i].end; written = parts[i].end;
} }
if (written < sourceLength) if (written < sourceLength)
@ -152,20 +160,20 @@ public final class Skin {
class Macro { class Macro {
int start, end; final int start, end;
String handler; String handler;
String name; String name;
String prefix; String prefix;
String suffix; String suffix;
HashMap parameters; String encoding;
String defaultValue;
Map parameters = null;
public Macro (int start, int end) { public Macro (int start, int end) {
this.start = start; this.start = start;
this.end = end; this.end = end;
parameters = new HashMap ();
int state = HANDLER; int state = HANDLER;
boolean escape = false; boolean escape = false;
char quotechar = '\u0000'; char quotechar = '\u0000';
@ -191,7 +199,9 @@ public final class Skin {
case '\'': case '\'':
if (!escape && state == PARAMVALUE) { if (!escape && state == PARAMVALUE) {
if (quotechar == source[i]) { if (quotechar == source[i]) {
parameters.put (lastParamName, b.toString()); String paramValue = b.toString();
if (!setSpecialParameter (lastParamName, paramValue))
addGenericParameter (lastParamName, paramValue);
lastParamName = null; lastParamName = null;
b.setLength (0); b.setLength (0);
state = PARAMNAME; state = PARAMNAME;
@ -215,7 +225,9 @@ public final class Skin {
b.setLength (0); b.setLength (0);
state = PARAMNAME; state = PARAMNAME;
} else if (state == PARAMVALUE && quotechar == '\u0000') { } else if (state == PARAMVALUE && quotechar == '\u0000') {
parameters.put (lastParamName, b.toString()); String paramValue = b.toString();
if (!setSpecialParameter (lastParamName, paramValue))
addGenericParameter (lastParamName, paramValue);
lastParamName = null; lastParamName = null;
b.setLength (0); b.setLength (0);
state = PARAMNAME; state = PARAMNAME;
@ -238,21 +250,43 @@ public final class Skin {
} }
} }
if (b.length() > 0) { if (b.length() > 0) {
if (lastParamName != null && b.length() > 0) if (lastParamName != null && b.length() > 0) {
parameters.put (lastParamName, b.toString()); String paramValue = b.toString();
else if (state <= MACRO) if (!setSpecialParameter (lastParamName, paramValue))
addGenericParameter (lastParamName, paramValue);
} else if (state <= MACRO)
name = b.toString().trim(); name = b.toString().trim();
} }
// get prefix and suffix from parameters
prefix = (String) parameters.get ("prefix");
suffix = (String) parameters.get ("suffix");
} }
private boolean setSpecialParameter (String name, String value) {
if ("prefix".equals (name)) {
prefix = value;
return true;
} else if ("suffix".equals (name)) {
suffix = value;
return true;
} else if ("encoding".equals (name)) {
encoding = value;
return true;
} else if ("default".equals (name)) {
defaultValue = value;
return true;
}
return false;
}
private void addGenericParameter (String name, String value) {
if (parameters == null)
parameters = new HashMap ();
parameters.put (name, value);
}
/** /**
* Render the macro given a handler object * Render the macro given a handler object
*/ */
public void render (RequestEvaluator reval, Object thisObject, HashMap paramObject) throws RedirectException { public void render (RequestEvaluator reval, Object thisObject, Map paramObject, Map handlerCache) throws RedirectException {
if (sandbox != null && !sandbox.contains (getFullName ())) { if (sandbox != null && !sandbox.contains (getFullName ())) {
String h = handler == null ? "global" : handler; String h = handler == null ? "global" : handler;
@ -273,51 +307,57 @@ public final class Skin {
Object handlerObject = null; Object handlerObject = null;
Object[] arguments = new Object[1];
// pass a clone of the parameter map so if the script changes it,
// we still keep the original version.
arguments[0] = parameters.clone ();
// flag to tell whether we found our invocation target object // flag to tell whether we found our invocation target object
boolean objectFound = true; boolean objectFound = true;
if (handler != null) { if (handler != null) {
if (thisObject != null) { // try to get handler from handlerCache first
// not a global macro - need to find handler object if (handlerCache != null)
// was called with this object - check it or its parents for matching prototype handlerObject = handlerCache.get (handler);
if (!handler.equalsIgnoreCase ("this") && !handler.equalsIgnoreCase (app.getPrototypeName (thisObject))) {
// the handler object is not what we want
Object n = thisObject;
// walk down parent chain to find handler object
while (n != null) {
if (handler.equalsIgnoreCase (app.getPrototypeName (n))) {
handlerObject = n;
break;
}
n = app.getParentElement (n);
}
} else {
// we already have the right handler object
handlerObject = thisObject;
}
}
if (handlerObject == null) { if (handlerObject == null) {
// eiter because thisObject == null or the right object wasn't found in the object's parent path
// go check request path for an object with matching prototype // if handler object wasn't found in cache retrieve it
int l = reval.requestPath.size(); if (handlerObject == null && thisObject != null) {
for (int i=l-1; i>=0; i--) { // not a global macro - need to find handler object
Object pathelem = reval.requestPath.get (i); // was called with this object - check it or its parents for matching prototype
if (handler.equalsIgnoreCase (app.getPrototypeName (pathelem))) { if (!handler.equals ("this") && !handler.equals (app.getPrototypeName (thisObject))) {
handlerObject = pathelem; // the handler object is not what we want
break; Object n = app.getParentElement (thisObject);
// walk down parent chain to find handler object
while (n != null) {
if (handler.equals (app.getPrototypeName (n))) {
handlerObject = n;
break;
}
n = app.getParentElement (n);
}
} else {
// we already have the right handler object
handlerObject = thisObject;
} }
} }
}
// the macro handler object couldn't be found if (handlerObject == null) {
if (handlerObject == null) // eiter because thisObject == null or the right object wasn't found in the object's parent path
objectFound = false; // go check request path for an object with matching prototype
int l = reval.requestPath.size();
for (int i=l-1; i>=0; i--) {
Object pathelem = reval.requestPath.get (i);
if (handler.equals (app.getPrototypeName (pathelem))) {
handlerObject = pathelem;
break;
}
}
}
// the macro handler object couldn't be found
if (handlerObject == null)
objectFound = false;
// else put the found handler object into the cache so we don't have to look again
else if (handlerCache != null)
handlerCache.put (handler, handlerObject);
}
} else { } else {
// this is a global macro with no handler specified // this is a global macro with no handler specified
@ -329,21 +369,35 @@ public final class Skin {
// if so, the macro evaluates to the function. Otherwise, // if so, the macro evaluates to the function. Otherwise,
// a property/field with the name is used, if defined. // a property/field with the name is used, if defined.
// remember length of response buffer before calling macro String funcName = name+"_macro";
int oldLength = reval.res.getBufferLength (); if (reval.scriptingEngine.hasFunction (handlerObject, funcName)) {
if (reval.scriptingEngine.hasFunction (handlerObject, name+"_macro")) { // remember length of response buffer before calling macro
int bufLength = reval.res.getBufferLength ();
// remember length of buffer with prefix written out
int preLength = 0;
if (prefix != null) {
reval.res.write (prefix);
preLength = prefix.length();
}
// System.err.println ("Getting macro from function"); // System.err.println ("Getting macro from function");
Object v = reval.scriptingEngine.invoke (handlerObject, name+"_macro", arguments, false); // pass a clone of the parameter map so if the script changes it,
// Map param = ;
Object[] arguments = { parameters == null ?
new HashMap () :
new HashMap (parameters) };
Object v = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false);
// check if macro wrote out to response buffer // check if macro wrote out to response buffer
if (reval.res.getBufferLength () > oldLength) { if (reval.res.getBufferLength () == bufLength + preLength) {
// insert prefix and append suffix // function didn't write out anything itself
if (prefix != null) if (preLength > 0)
reval.res.insert (oldLength, prefix); reval.res.setBufferLength (bufLength);
if (suffix != null) writeToResponse (v, reval.res, true);
} else {
if (suffix != null)
reval.res.write (suffix); reval.res.write (suffix);
writeToResponse (v, reval.res, false); writeToResponse (v, reval.res, false);
} else {
writeToResponse (v, reval.res, true);
} }
} else { } else {
// System.err.println ("Getting macro from property"); // System.err.println ("Getting macro from property");
@ -392,7 +446,7 @@ public final class Skin {
writeToResponse (value, reval.res, true); writeToResponse (value, reval.res, true);
} }
private void renderFromParam (RequestEvaluator reval, HashMap paramObject) { private void renderFromParam (RequestEvaluator reval, Map paramObject) {
if (paramObject == null) if (paramObject == null)
reval.res.write ("[HopMacro error: Skin requires a parameter object]"); reval.res.write ("[HopMacro error: Skin requires a parameter object]");
else { else {
@ -408,7 +462,7 @@ public final class Skin {
String text; String text;
if (value == null) { if (value == null) {
if (useDefault) if (useDefault)
text = (String) parameters.get ("default"); text = defaultValue;
else else
return; return;
} else { } else {
@ -416,9 +470,10 @@ public final class Skin {
} }
if (text == null || text.length() == 0) if (text == null || text.length() == 0)
return; return;
String encoding = (String) parameters.get ("encoding"); if (encoding != null)
text = encode (text, encoding);
res.write (prefix); res.write (prefix);
res.write (encode (text, encoding)); res.write (text);
res.write (suffix); res.write (suffix);
} }
@ -427,8 +482,6 @@ public final class Skin {
* encodings on the macro output. * encodings on the macro output.
*/ */
String encode (String text, String encoding) { String encode (String text, String encoding) {
if (encoding == null || text == null)
return text;
if ("html".equalsIgnoreCase (encoding)) if ("html".equalsIgnoreCase (encoding))
return HtmlEncoder.encode (text); return HtmlEncoder.encode (text);
if ("xml".equalsIgnoreCase (encoding)) if ("xml".equalsIgnoreCase (encoding))