* Refactor and enhance Skin class:
- implement macro/filter pipes <% foo | bar %> - implement deep macros <% foo.bar.foo %> - implement nested macros <% foo x=<% bar %> %> - implement failmode=silent|verbose attribute * Refactor ScriptingEngine interface and implementation to support new skinning features.
This commit is contained in:
		
							parent
							
								
									1ef63471aa
								
							
						
					
					
						commit
						e4784f870d
					
				
					 3 changed files with 471 additions and 282 deletions
				
			
		|  | @ -20,7 +20,6 @@ import helma.framework.*; | ||||||
| import helma.framework.repository.Resource; | import helma.framework.repository.Resource; | ||||||
| import helma.objectmodel.ConcurrencyException; | import helma.objectmodel.ConcurrencyException; | ||||||
| import helma.util.*; | import helma.util.*; | ||||||
| import helma.scripting.ScriptingException; |  | ||||||
| 
 | 
 | ||||||
| import java.util.*; | import java.util.*; | ||||||
| import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||||
|  | @ -34,16 +33,25 @@ import java.io.IOException; | ||||||
|  * from the RequestEvaluator object to resolve Macro handlers by type name. |  * from the RequestEvaluator object to resolve Macro handlers by type name. | ||||||
|  */ |  */ | ||||||
| public final class Skin { | public final class Skin { | ||||||
|     static final int HANDLER = 0; |     static private final int PARSE_MACRONAME = 0; | ||||||
|     static final int MACRO = 1; |     static private final int PARSE_PARAMNAME = 1; | ||||||
|     static final int PARAMNAME = 2; |     static private final int PARSE_PARAMVALUE = 2; | ||||||
|     static final int PARAMVALUE = 3; | 
 | ||||||
|     static final int ENCODE_NONE = 0; |     static private final int ENCODE_NONE = 0; | ||||||
|     static final int ENCODE_HTML = 1; |     static private final int ENCODE_HTML = 1; | ||||||
|     static final int ENCODE_XML = 2; |     static private final int ENCODE_XML = 2; | ||||||
|     static final int ENCODE_FORM = 3; |     static private final int ENCODE_FORM = 3; | ||||||
|     static final int ENCODE_URL = 4; |     static private final int ENCODE_URL = 4; | ||||||
|     static final int ENCODE_ALL = 5; |     static private final int ENCODE_ALL = 5; | ||||||
|  | 
 | ||||||
|  |     static private final int HANDLER_RESPONSE = 0; | ||||||
|  |     static private final int HANDLER_REQUEST = 1; | ||||||
|  |     static private final int HANDLER_SESSION = 2; | ||||||
|  |     static private final int HANDLER_PARAM = 3; | ||||||
|  |     static private final int HANDLER_GLOBAL = 4; | ||||||
|  |     static private final int HANDLER_THIS = 5; | ||||||
|  |     static private final int HANDLER_OTHER = 6; | ||||||
|  | 
 | ||||||
|     private Macro[] macros; |     private Macro[] macros; | ||||||
|     private Application app; |     private Application app; | ||||||
|     private char[] source; |     private char[] source; | ||||||
|  | @ -114,22 +122,15 @@ public final class Skin { | ||||||
|     private void parse() { |     private void parse() { | ||||||
|         ArrayList partBuffer = new ArrayList(); |         ArrayList partBuffer = new ArrayList(); | ||||||
| 
 | 
 | ||||||
|  |         boolean escape = false; | ||||||
|         for (int i = 0; i < (sourceLength - 1); i++) { |         for (int i = 0; i < (sourceLength - 1); i++) { | ||||||
|             if ((source[i] == '<') && (source[i + 1] == '%')) { |             if (source[i] == '<' && source[i + 1] == '%' && !escape) { | ||||||
|                 // found macro start tag |                 // found macro start tag | ||||||
|                 int j = i + 2; |                 Macro macro = new Macro(i, 2); | ||||||
| 
 |                 partBuffer.add(macro); | ||||||
|                 // search macr end tag |                 i = macro.end - 1; | ||||||
|                 while ((j < (sourceLength - 1)) && |             } else { | ||||||
|                            ((source[j] != '%') || (source[j + 1] != '>'))) { |                 escape = source[i] == '\\' && !escape; | ||||||
|                     j++; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (j > (i + 2)) { |  | ||||||
|                     partBuffer.add(new Macro(i, j + 2)); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 i = j + 1; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -222,7 +223,7 @@ public final class Skin { | ||||||
|             if (macros[i] instanceof Macro) { |             if (macros[i] instanceof Macro) { | ||||||
|                 Macro m = macros[i]; |                 Macro m = macros[i]; | ||||||
| 
 | 
 | ||||||
|                 if (macroname.equals(m.fullName)) { |                 if (macroname.equals(m.name)) { | ||||||
|                     return true; |                     return true; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -242,58 +243,113 @@ public final class Skin { | ||||||
|         sandbox.add(macroname); |         sandbox.add(macroname); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private Object renderMacro(Object value, RequestEvaluator reval, | ||||||
|  |                                Object thisObj, Map handlerCache) | ||||||
|  |             throws UnsupportedEncodingException { | ||||||
|  |         if (value instanceof Macro) { | ||||||
|  |             ResponseTrans res = reval.getResponse(); | ||||||
|  |             res.pushStringBuffer(); | ||||||
|  |             ((Macro) value).render(reval, thisObj, handlerCache); | ||||||
|  |             return res.popStringBuffer(); | ||||||
|  |         } else { | ||||||
|  |             return value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     class Macro { |     class Macro { | ||||||
|         final int start; |         final int start, end; | ||||||
|         final int end; |  | ||||||
|         String handler; |  | ||||||
|         String name; |         String name; | ||||||
|         String fullName; |         String[] path; | ||||||
|  |         int handler = HANDLER_OTHER; | ||||||
|         int encoding = ENCODE_NONE; |         int encoding = ENCODE_NONE; | ||||||
|  |         boolean hasNestedMacros = false; | ||||||
| 
 | 
 | ||||||
|         // default render parameters - may be overridden if macro changes |         // default render parameters - may be overridden if macro changes | ||||||
|         // param.prefix/suffix/default |         // param.prefix/suffix/default | ||||||
|         RenderParameters defaultRenderParams = new RenderParameters(); |         StandardParams standardParams = new StandardParams(); | ||||||
|         Map params = null; |         Map params = null; | ||||||
|  |         // filters defined via <% foo | bar %> | ||||||
|  |         Macro filterChain; | ||||||
| 
 | 
 | ||||||
|         // comment macros are silently dropped during rendering |         // comment macros are silently dropped during rendering | ||||||
|         boolean isCommentMacro = false; |         boolean isCommentMacro = false; | ||||||
| 
 | 
 | ||||||
|         public Macro(int start, int end) { |         /** | ||||||
|  |          * Create and parse a new macro. | ||||||
|  |          * @param start the start of the macro within the skin source | ||||||
|  |          * @param offset offset of the macro content from the start index | ||||||
|  |          */ | ||||||
|  |         public Macro(int start, int offset) { | ||||||
|             this.start = start; |             this.start = start; | ||||||
|             this.end = end; |  | ||||||
| 
 | 
 | ||||||
|             int state = HANDLER; |             int state = PARSE_MACRONAME; | ||||||
|             boolean escape = false; |             boolean escape = false; | ||||||
|             char quotechar = '\u0000'; |             char quotechar = '\u0000'; | ||||||
|             String lastParamName = null; |             String lastParamName = null; | ||||||
|             StringBuffer b = new StringBuffer(); |             StringBuffer b = new StringBuffer(); | ||||||
|  |             int i; | ||||||
|  | 
 | ||||||
|  |             loop: | ||||||
|  |             for (i = start + offset; i < sourceLength - 1; i++) { | ||||||
| 
 | 
 | ||||||
|             for (int i = start + 2; i < (end - 2); i++) { |  | ||||||
|                 switch (source[i]) { |                 switch (source[i]) { | ||||||
| 
 | 
 | ||||||
|  |                     case '<': | ||||||
|  | 
 | ||||||
|  |                         if (state == PARSE_PARAMVALUE && quotechar == '\u0000' && source[i + 1] == '%') { | ||||||
|  |                             Macro macro = new Macro(i, 2); | ||||||
|  |                             hasNestedMacros = true; | ||||||
|  |                             addParameter(lastParamName, macro); | ||||||
|  |                             lastParamName = null; | ||||||
|  |                             b.setLength(0); | ||||||
|  |                             state = PARSE_PARAMNAME; | ||||||
|  |                             i = macro.end - 1; | ||||||
|  |                         } else { | ||||||
|  |                             b.append(source[i]); | ||||||
|  |                             escape = false; | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     case '%': | ||||||
|  | 
 | ||||||
|  |                         if ((state != PARSE_PARAMVALUE || quotechar == '\u0000') && source[i + 1] == '>') { | ||||||
|  |                             break loop; | ||||||
|  |                         } | ||||||
|  |                         b.append(source[i]); | ||||||
|  |                         escape = false; | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|                     case '/': |                     case '/': | ||||||
| 
 | 
 | ||||||
|                         b.append(source[i]); |                         b.append(source[i]); | ||||||
|                         escape = false; |                         escape = false; | ||||||
| 
 | 
 | ||||||
|                         if (state == HANDLER && "//".equals(b.toString())) { |                         if (state == PARSE_MACRONAME && "//".equals(b.toString())) { | ||||||
|                             isCommentMacro = true; |                             isCommentMacro = true; | ||||||
|                             return; |                             // search macro end tag | ||||||
|  |                             while (i < sourceLength - 1 && | ||||||
|  |                                        (source[i] != '%' || source[i + 1] != '>')) { | ||||||
|  |                                 i++; | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             break loop; | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         break; |                         break; | ||||||
| 
 | 
 | ||||||
|                     case '.': |                     case '|': | ||||||
| 
 | 
 | ||||||
|                         if (state == HANDLER) { |                         if (state == PARSE_PARAMNAME) { | ||||||
|                             handler = b.toString().trim(); |                             filterChain = new Macro(i, 1); | ||||||
|  |                             i = filterChain.end - 2; | ||||||
|  |                             lastParamName = null; | ||||||
|                             b.setLength(0); |                             b.setLength(0); | ||||||
|                             state = MACRO; |                             break loop; | ||||||
|                         } else { |                         } | ||||||
|                         b.append(source[i]); |                         b.append(source[i]); | ||||||
|                         escape = false; |                         escape = false; | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         break; |                         break; | ||||||
| 
 | 
 | ||||||
|                     case '\\': |                     case '\\': | ||||||
|  | @ -309,13 +365,13 @@ public final class Skin { | ||||||
|                     case '"': |                     case '"': | ||||||
|                     case '\'': |                     case '\'': | ||||||
| 
 | 
 | ||||||
|                         if (!escape && (state == PARAMVALUE)) { |                         if (!escape && (state == PARSE_PARAMVALUE)) { | ||||||
|                             if (quotechar == source[i]) { |                             if (quotechar == source[i]) { | ||||||
|                                 // add parameter |                                 // add parameter | ||||||
|                                 addParameter(lastParamName, b.toString()); |                                 addParameter(lastParamName, b.toString()); | ||||||
|                                 lastParamName = null; |                                 lastParamName = null; | ||||||
|                                 b.setLength(0); |                                 b.setLength(0); | ||||||
|                                 state = PARAMNAME; |                                 state = PARSE_PARAMNAME; | ||||||
|                                 quotechar = '\u0000'; |                                 quotechar = '\u0000'; | ||||||
|                             } else if (quotechar == '\u0000') { |                             } else if (quotechar == '\u0000') { | ||||||
|                                 quotechar = source[i]; |                                 quotechar = source[i]; | ||||||
|  | @ -337,17 +393,17 @@ public final class Skin { | ||||||
|                     case '\r': |                     case '\r': | ||||||
|                     case '\f': |                     case '\f': | ||||||
| 
 | 
 | ||||||
|                         if ((state == MACRO) || ((state == HANDLER) && (b.length() > 0))) { |                         if (state == PARSE_MACRONAME && b.length() > 0) { | ||||||
|                             name = b.toString().trim(); |                             name = b.toString().trim(); | ||||||
|                             b.setLength(0); |                             b.setLength(0); | ||||||
|                             state = PARAMNAME; |                             state = PARSE_PARAMNAME; | ||||||
|                         } else if ((state == PARAMVALUE) && (quotechar == '\u0000')) { |                         } else if ((state == PARSE_PARAMVALUE) && (quotechar == '\u0000')) { | ||||||
|                             // add parameter |                             // add parameter | ||||||
|                             addParameter(lastParamName, b.toString()); |                             addParameter(lastParamName, b.toString()); | ||||||
|                             lastParamName = null; |                             lastParamName = null; | ||||||
|                             b.setLength(0); |                             b.setLength(0); | ||||||
|                             state = PARAMNAME; |                             state = PARSE_PARAMNAME; | ||||||
|                         } else if (state == PARAMVALUE) { |                         } else if (state == PARSE_PARAMVALUE) { | ||||||
|                             b.append(source[i]); |                             b.append(source[i]); | ||||||
|                             escape = false; |                             escape = false; | ||||||
|                         } else { |                         } else { | ||||||
|  | @ -358,10 +414,10 @@ public final class Skin { | ||||||
| 
 | 
 | ||||||
|                     case '=': |                     case '=': | ||||||
| 
 | 
 | ||||||
|                         if (state == PARAMNAME) { |                         if (state == PARSE_PARAMNAME) { | ||||||
|                             lastParamName = b.toString().trim(); |                             lastParamName = b.toString().trim(); | ||||||
|                             b.setLength(0); |                             b.setLength(0); | ||||||
|                             state = PARAMVALUE; |                             state = PARSE_PARAMVALUE; | ||||||
|                         } else { |                         } else { | ||||||
|                             b.append(source[i]); |                             b.append(source[i]); | ||||||
|                             escape = false; |                             escape = false; | ||||||
|  | @ -375,42 +431,65 @@ public final class Skin { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             this.end = Math.min(sourceLength, i + 2); | ||||||
|  | 
 | ||||||
|             if (b.length() > 0) { |             if (b.length() > 0) { | ||||||
|                 if (lastParamName != null) { |                 if (lastParamName != null) { | ||||||
|                     // add parameter |                     // add parameter | ||||||
|                     addParameter(lastParamName, b.toString()); |                     addParameter(lastParamName, b.toString()); | ||||||
|                 } else if (state <= MACRO) { |                 } else if (state == PARSE_MACRONAME) { | ||||||
|                     name = b.toString().trim(); |                     name = b.toString().trim(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (handler == null) { |             path = StringUtils.split(name, "."); | ||||||
|                 fullName = name; |             if (path.length <= 1) { | ||||||
|  |                 handler = HANDLER_GLOBAL; | ||||||
|             } else { |             } else { | ||||||
|                 fullName = handler + "." + name; |                 String handlerName = path[0]; | ||||||
|  |                 if ("this".equalsIgnoreCase(handlerName)) { | ||||||
|  |                     handler = HANDLER_THIS; | ||||||
|  |                 } else if ("response".equalsIgnoreCase(handlerName)) { | ||||||
|  |                     handler = HANDLER_RESPONSE; | ||||||
|  |                 } else if ("request".equalsIgnoreCase(handlerName)) { | ||||||
|  |                     handler = HANDLER_REQUEST; | ||||||
|  |                 } else if ("session".equalsIgnoreCase(handlerName)) { | ||||||
|  |                     handler = HANDLER_SESSION; | ||||||
|  |                 } else if ("param".equalsIgnoreCase(handlerName)) { | ||||||
|  |                     handler = HANDLER_PARAM; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // Set default failmode unless explicitly set: | ||||||
|  |             // silent for default handlers, verbose | ||||||
|  |             if (params == null || !params.containsKey("failmode")) { | ||||||
|  |                 standardParams.silentFailMode = (handler < HANDLER_GLOBAL); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void addParameter(String name, String value) { |         private void addParameter(String name, Object value) { | ||||||
|             // check if this is parameter is relevant to us |             // check if this is parameter is relevant to us | ||||||
|             if ("prefix".equals(name)) { |             if ("prefix".equals(name)) { | ||||||
|                 defaultRenderParams.prefix = value; |                 standardParams.prefix = value; | ||||||
|             } else if ("suffix".equals(name)) { |             } else if ("suffix".equals(name)) { | ||||||
|                 defaultRenderParams.suffix = value; |                 standardParams.suffix = value; | ||||||
|             } else if ("encoding".equals(name)) { |             } else if ("encoding".equals(name)) { | ||||||
|                 if ("html".equalsIgnoreCase(value)) { |                 if ("html".equals(value)) { | ||||||
|                     encoding = ENCODE_HTML; |                     encoding = ENCODE_HTML; | ||||||
|                 } else if ("xml".equalsIgnoreCase(value)) { |                 } else if ("xml".equals(value)) { | ||||||
|                     encoding = ENCODE_XML; |                     encoding = ENCODE_XML; | ||||||
|                 } else if ("form".equalsIgnoreCase(value)) { |                 } else if ("form".equals(value)) { | ||||||
|                     encoding = ENCODE_FORM; |                     encoding = ENCODE_FORM; | ||||||
|                 } else if ("url".equalsIgnoreCase(value)) { |                 } else if ("url".equals(value)) { | ||||||
|                     encoding = ENCODE_URL; |                     encoding = ENCODE_URL; | ||||||
|                 } else if ("all".equalsIgnoreCase(value)) { |                 } else if ("all".equals(value)) { | ||||||
|                     encoding = ENCODE_ALL; |                     encoding = ENCODE_ALL; | ||||||
|  |                 } else { | ||||||
|  |                     app.logEvent("Unrecognized encoding in skin macro: " + value); | ||||||
|                 } |                 } | ||||||
|             } else if ("default".equals(name)) { |             } else if ("default".equals(name)) { | ||||||
|                 defaultRenderParams.defaultValue = value; |                 standardParams.defaultValue = value; | ||||||
|  |             } else if ("failmode".equals(name)) { | ||||||
|  |                 standardParams.setFailMode(value); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Add parameter to parameter map |             // Add parameter to parameter map | ||||||
|  | @ -430,94 +509,29 @@ public final class Skin { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if ((sandbox != null) && !sandbox.contains(fullName)) { |             if ((sandbox != null) && !sandbox.contains(name)) { | ||||||
|                 reval.getResponse().write("[Macro " + fullName + " not allowed in sandbox]"); |                 throw new RuntimeException("Macro " + name + " not allowed in sandbox"); | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } else if ("response".equalsIgnoreCase(handler)) { |  | ||||||
|                 renderFromResponse(reval); |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } else if ("request".equalsIgnoreCase(handler)) { |  | ||||||
|                 renderFromRequest(reval); |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } else if ("session".equalsIgnoreCase(handler)) { |  | ||||||
|                 renderFromSession(reval); |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             try { |  | ||||||
|             Object handlerObject = null; |             Object handlerObject = null; | ||||||
| 
 | 
 | ||||||
|                 // flag to tell whether we found our invocation target object |             try { | ||||||
|                 boolean objectFound = true; |  | ||||||
| 
 | 
 | ||||||
|                 if (handler != null) { |                 if (handler != HANDLER_GLOBAL) { | ||||||
|                     // try to get handler from handlerCache first |                     handlerObject = resolveHandler(thisObject, reval, handlerCache); | ||||||
|                     if (handlerCache != null) { |                     handlerObject = resolvePath(handlerObject, reval); | ||||||
|                         handlerObject = handlerCache.get(handler); |  | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                     if (handlerObject == null) { |                 if (handler == HANDLER_GLOBAL || handlerObject != null) { | ||||||
|                         // if handler object wasn't found in cache retrieve it |  | ||||||
|                         if ((handlerObject == null) && (thisObject != null)) { |  | ||||||
|                             // not a global macro - need to find handler object |  | ||||||
|                             // was called with this object - check it or its parents for matching prototype |  | ||||||
|                             if (!handler.equals("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) { |  | ||||||
|                                     Prototype proto = app.getPrototype(n); |  | ||||||
| 
 |  | ||||||
|                                     if ((proto != null) && proto.isInstanceOf(handler)) { |  | ||||||
|                                         handlerObject = n; |  | ||||||
| 
 |  | ||||||
|                                         break; |  | ||||||
|                                     } |  | ||||||
| 
 |  | ||||||
|                                     n = app.getParentElement(n); |  | ||||||
|                                 } |  | ||||||
|                             } else { |  | ||||||
|                                 // we already have the right handler object |  | ||||||
|                                 handlerObject = thisObject; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (handlerObject == null) { |  | ||||||
|                             // eiter because thisObject == null or the right object wasn't found |  | ||||||
|                             // in the object's parent path. Check if a matching macro handler |  | ||||||
|                             // is registered with the response object (res.handlers). |  | ||||||
|                             handlerObject = reval.getResponse().getMacroHandlers().get(handler); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         // 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 { |  | ||||||
|                     // this is a global macro with no handler specified |  | ||||||
|                     handlerObject = null; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (objectFound) { |  | ||||||
|                     // check if a function called name_macro is defined. |                     // check if a function called name_macro is defined. | ||||||
|                     // 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. | ||||||
|                     String funcName = name + "_macro"; |                     String propName = path[path.length - 1]; | ||||||
|  |                     String funcName = propName + "_macro"; | ||||||
| 
 | 
 | ||||||
|                     if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) { |                     if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) { | ||||||
|                         StringBuffer buffer = reval.getResponse().getBuffer(); |                         StringBuffer buffer = reval.getResponse().getBuffer(); | ||||||
|                         RenderParameters renderParams = defaultRenderParams; |                         StandardParams stdParams = standardParams; | ||||||
| 
 | 
 | ||||||
|                         // remember length of response buffer before calling macro |                         // remember length of response buffer before calling macro | ||||||
|                         int bufLength = buffer.length(); |                         int bufLength = buffer.length(); | ||||||
|  | @ -528,67 +542,100 @@ public final class Skin { | ||||||
| 
 | 
 | ||||||
|                         if (params == null) { |                         if (params == null) { | ||||||
|                             arguments = new Object[] { new SystemMap(4) }; |                             arguments = new Object[] { new SystemMap(4) }; | ||||||
|  |                         } else if (hasNestedMacros) { | ||||||
|  |                             SystemMap map = new SystemMap((int) (params.size() * 1.5)); | ||||||
|  |                             ResponseTrans res = reval.getResponse(); | ||||||
|  |                             for (Iterator it = params.entrySet().iterator(); it.hasNext(); ) { | ||||||
|  |                                 Map.Entry entry = (Map.Entry) it.next(); | ||||||
|  |                                 Object value = entry.getValue(); | ||||||
|  |                                 map.put(entry.getKey(), renderMacro(value, reval, thisObject, handlerCache)); | ||||||
|  |                             } | ||||||
|  |                             if (standardParams.containsMacros()) | ||||||
|  |                                 stdParams = new StandardParams(map); | ||||||
|  |                             arguments = new Object[] { map }; | ||||||
|                         } else { |                         } else { | ||||||
|                             wrappedParams = new CopyOnWriteMap(params); |                             wrappedParams = new CopyOnWriteMap(params); | ||||||
|                             arguments = new Object[] { wrappedParams }; |                             arguments = new Object[] { wrappedParams }; | ||||||
|                         } |                         } | ||||||
| 
 |  | ||||||
|                         Object value = reval.invokeDirectFunction(handlerObject, |                         Object value = reval.invokeDirectFunction(handlerObject, | ||||||
|                                                                   funcName, |                                                                   funcName, | ||||||
|                                                                   arguments); |                                                                   arguments); | ||||||
| 
 | 
 | ||||||
|                         // if parameter map was modified create new renderParams to override defaults |                         // if parameter map was modified create new renderParams to override defaults | ||||||
|                         if (wrappedParams != null && wrappedParams.wasModified()) { |                         if (wrappedParams != null && wrappedParams.wasModified()) { | ||||||
|                             renderParams = new RenderParameters(wrappedParams); |                             stdParams = new StandardParams(wrappedParams); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         // check if this macro has a filter chain | ||||||
|  |                         if (filterChain != null) { | ||||||
|  |                             if (value == null && buffer.length() > bufLength) { | ||||||
|  |                                 value = buffer.substring(bufLength); | ||||||
|  |                                 buffer.setLength(bufLength); | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         // check if macro wrote out to response buffer |                         // check if macro wrote out to response buffer | ||||||
|                         if (buffer.length() == bufLength) { |                         if (buffer.length() == bufLength) { | ||||||
|                             // If the macro function didn't write anything to the response itself, |                             // If the macro function didn't write anything to the response itself, | ||||||
|                             // we interpret its return value as macro output. |                             // we interpret its return value as macro output. | ||||||
|                             writeResponse(value, buffer, renderParams, true); |                             writeResponse(value, reval, thisObject, handlerCache, stdParams, true); | ||||||
|                         } else { |                         } else { | ||||||
|                             // if an encoding is specified, re-encode the macro's output |                             // if an encoding is specified, re-encode the macro's output | ||||||
|                             if (encoding != ENCODE_NONE) { |                             if (encoding != ENCODE_NONE) { | ||||||
|                                 String output = buffer.substring(bufLength); |                                 String output = buffer.substring(bufLength); | ||||||
| 
 | 
 | ||||||
|                                 buffer.setLength(bufLength); |                                 buffer.setLength(bufLength); | ||||||
|                                 writeResponse(output, buffer, renderParams, false); |                                 writeResponse(output, reval, thisObject, handlerCache, stdParams, false); | ||||||
|                             } else { |                             } else { | ||||||
|                                 // insert prefix, |                                 // insert prefix, | ||||||
|                                 if (renderParams.prefix != null) { |                                 if (stdParams.prefix != null) { | ||||||
|                                     buffer.insert(bufLength, renderParams.prefix); |                                     buffer.insert(bufLength, stdParams.prefix); | ||||||
|                                 } |                                 } | ||||||
|                                 // append suffix |                                 // append suffix | ||||||
|                                 if (renderParams.suffix != null) { |                                 if (stdParams.suffix != null) { | ||||||
|                                     buffer.append(renderParams.suffix); |                                     buffer.append(stdParams.suffix); | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
| 
 | 
 | ||||||
|                             // Append macro return value even if it wrote something to the response, |                             // Append macro return value even if it wrote something to the response, | ||||||
|                             // but don't render default value in case it returned nothing. |                             // but don't render default value in case it returned nothing. | ||||||
|                             // We do this for the sake of consistency. |                             // We do this for the sake of consistency. | ||||||
|                             writeResponse(value, buffer, renderParams, false); |                             writeResponse(value, reval, thisObject, handlerCache, stdParams, false); | ||||||
|                         } |                         } | ||||||
|                     } else { |                     } else { | ||||||
|                         // for unhandled global macros display error message, |                         if (handler == HANDLER_RESPONSE) { | ||||||
|                         // otherwise try property lookup |                             // some special handling for response handler | ||||||
|                         if (handlerObject == null) { |                             Object value = null; | ||||||
|                             String msg = "[Macro unhandled: " + fullName + "]"; |                             if ("message".equals(propName)) { | ||||||
|  |                                 value = reval.getResponse().getMessage(); | ||||||
|  |                             } else if ("error".equals(propName)) { | ||||||
|  |                                 value = reval.getResponse().getError(); | ||||||
|  |                             } | ||||||
|  |                             if (value != null) { | ||||||
|  |                                 writeResponse(value, reval, thisObject, handlerCache, standardParams, true); | ||||||
|  |                                 return;                                 | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         // display error message unless silent failmode is on | ||||||
|  |                         if (handlerObject == null || !hasProperty(handlerObject, propName, reval)) { | ||||||
|  |                             if (!standardParams.silentFailMode) { | ||||||
|  |                                 String msg = "[Macro unhandled: " + name + "]"; | ||||||
|                                 reval.getResponse().write(" " + msg + " "); |                                 reval.getResponse().write(" " + msg + " "); | ||||||
|                                 app.logEvent(msg); |                                 app.logEvent(msg); | ||||||
|  |                             } | ||||||
| 
 | 
 | ||||||
|                         } else { |                         } else { | ||||||
|                             Object value = reval.scriptingEngine.get(handlerObject, name); |                             Object value = getProperty(handlerObject, propName, reval); | ||||||
|                             writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true); |                             writeResponse(value, reval, thisObject, handlerCache, standardParams, true); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                 } else { |                 } else { | ||||||
|                     String msg = "[Macro unhandled: " + fullName + "]"; |                     if (!standardParams.silentFailMode) { | ||||||
|  |                         String msg = "[Macro unhandled: " + name + "]"; | ||||||
|                         reval.getResponse().write(" " + msg + " "); |                         reval.getResponse().write(" " + msg + " "); | ||||||
|                         app.logEvent(msg); |                         app.logEvent(msg); | ||||||
| 
 |                     } | ||||||
|                 } |                 } | ||||||
|             } catch (RedirectException redir) { |             } catch (RedirectException redir) { | ||||||
|                 throw redir; |                 throw redir; | ||||||
|  | @ -601,98 +648,176 @@ public final class Skin { | ||||||
|                 if ((msg == null) || (msg.length() < 10)) { |                 if ((msg == null) || (msg.length() < 10)) { | ||||||
|                     msg = x.toString(); |                     msg = x.toString(); | ||||||
|                 } |                 } | ||||||
|                 msg = new StringBuffer("Macro error in ").append(fullName) |                 msg = new StringBuffer("Macro error in ").append(name) | ||||||
|                         .append(": ").append(msg).toString(); |                         .append(": ").append(msg).toString(); | ||||||
|                 reval.getResponse().write(" [" + msg + "] "); |                 reval.getResponse().write(" [" + msg + "] "); | ||||||
|                 app.logError(msg, x); |                 app.logError(msg, x); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void renderFromResponse(RequestEvaluator reval) |         private Object invokeFilter(RequestEvaluator reval, Object returnValue, | ||||||
|                 throws UnsupportedEncodingException { |                                     Object thisObject, Map handlerCache) | ||||||
|             Object value = null; |                 throws Exception { | ||||||
| 
 | 
 | ||||||
|             if ("message".equals(name)) { |             if ((sandbox != null) && !sandbox.contains(name)) { | ||||||
|                 value = reval.getResponse().getMessage(); |                 throw new RuntimeException("Macro " + name + " not allowed in sandbox"); | ||||||
|             } else if ("error".equals(name)) { |             } | ||||||
|                 value = reval.getResponse().getError(); |             Object handlerObject = null; | ||||||
|  | 
 | ||||||
|  |             if (handler != HANDLER_GLOBAL) { | ||||||
|  |                 handlerObject = resolveHandler(thisObject, reval, handlerCache); | ||||||
|  |                 handlerObject = resolvePath(handlerObject, reval); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (value == null) { |             String funcName = name + "_filter"; | ||||||
|                 value = reval.getResponse().get(name); | 
 | ||||||
|  |             if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) { | ||||||
|  |                 // pass a clone/copy of the parameter map so if the script changes it, | ||||||
|  |                 CopyOnWriteMap wrappedParams = null; | ||||||
|  |                 Object[] arguments; | ||||||
|  | 
 | ||||||
|  |                 if (params == null) { | ||||||
|  |                     arguments = new Object[] { returnValue, new SystemMap(4) }; | ||||||
|  |                 } else if (hasNestedMacros) { | ||||||
|  |                     SystemMap map = new SystemMap((int) (params.size() * 1.5)); | ||||||
|  |                     ResponseTrans res = reval.getResponse(); | ||||||
|  |                     for (Iterator it = params.entrySet().iterator(); it.hasNext(); ) { | ||||||
|  |                         Map.Entry entry = (Map.Entry) it.next(); | ||||||
|  |                         Object value = entry.getValue(); | ||||||
|  |                         map.put(entry.getKey(), renderMacro(value, reval, thisObject, handlerCache)); | ||||||
|                     } |                     } | ||||||
| 
 |                     arguments = new Object[] { map }; | ||||||
|             writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void renderFromRequest(RequestEvaluator reval) |  | ||||||
|                 throws UnsupportedEncodingException { |  | ||||||
|             if (reval.getRequest() == null) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Object value = reval.getRequest().get(name); |  | ||||||
| 
 |  | ||||||
|             writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void renderFromSession(RequestEvaluator reval) |  | ||||||
|                 throws UnsupportedEncodingException { |  | ||||||
|             if (reval.getSession() == null) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Object value = reval.getSession().getCacheNode().getString(name); |  | ||||||
| 
 |  | ||||||
|             writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void renderFromParam(RequestEvaluator reval, Map paramObject) |  | ||||||
|                 throws UnsupportedEncodingException { |  | ||||||
|             if (paramObject == null) { |  | ||||||
|                 reval.getResponse().write("[Macro error: Skin requires a parameter object]"); |  | ||||||
|                 } else { |                 } else { | ||||||
|                 Object value = paramObject.get(name); |                     wrappedParams = new CopyOnWriteMap(params); | ||||||
|  |                     arguments = new Object[] { returnValue, wrappedParams }; | ||||||
|  |                 } | ||||||
|  |                 Object retval = reval.invokeDirectFunction(handlerObject, | ||||||
|  |                                                            funcName, | ||||||
|  |                                                            arguments); | ||||||
|  |                 if (filterChain == null) { | ||||||
|  |                     return retval; | ||||||
|  |                 } | ||||||
|  |                 return filterChain.invokeFilter(reval, retval, thisObject, handlerCache); | ||||||
|  |             } else { | ||||||
|  |                 throw new RuntimeException("[Undefined Filter: " + name + "]"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|                 writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true); |         private Object resolveHandler(Object thisObject, RequestEvaluator reval, Map handlerCache) { | ||||||
|  |             if (path.length < 2) | ||||||
|  |                 throw new RuntimeException("resolveHandler called on global macro"); | ||||||
|  | 
 | ||||||
|  |             switch (handler) { | ||||||
|  |                 case HANDLER_THIS: | ||||||
|  |                     return thisObject; | ||||||
|  |                 case HANDLER_RESPONSE: | ||||||
|  |                     return reval.getResponse().getResponseData(); | ||||||
|  |                 case HANDLER_REQUEST: | ||||||
|  |                     return reval.getRequest().getRequestData(); | ||||||
|  |                 case HANDLER_SESSION: | ||||||
|  |                     return reval.getSession().getCacheNode(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             String handlerName = path[0]; | ||||||
|  |             // try to get handler from handlerCache first | ||||||
|  |             if (handlerCache != null && handlerCache.containsKey(handlerName)) { | ||||||
|  |                 return handlerCache.get(handlerName); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // if handler object wasn't found in cache retrieve it | ||||||
|  |             if (thisObject != null) { | ||||||
|  |                 // not a global macro - need to find handler object | ||||||
|  |                 // was called with this object - check it or its parents for matching prototype | ||||||
|  |                 if (handlerName.equalsIgnoreCase(app.getPrototypeName(thisObject))) { | ||||||
|  |                     // we already have the right handler object | ||||||
|  |                     // put the found handler object into the cache so we don't have to look again | ||||||
|  |                     if (handlerCache != null) | ||||||
|  |                         handlerCache.put(handlerName, thisObject); | ||||||
|  |                     return thisObject; | ||||||
|  |                 } else { | ||||||
|  |                     // the handler object is not what we want | ||||||
|  |                     Object obj = thisObject; | ||||||
|  | 
 | ||||||
|  |                     // walk down parent chain to find handler object | ||||||
|  |                     while (obj != null) { | ||||||
|  |                         Prototype proto = app.getPrototype(obj); | ||||||
|  | 
 | ||||||
|  |                         if ((proto != null) && proto.isInstanceOf(handlerName)) { | ||||||
|  |                             if (handlerCache != null) | ||||||
|  |                                 handlerCache.put(handlerName, obj); | ||||||
|  |                             return obj; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         obj = app.getParentElement(obj); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Map macroHandlers = reval.getResponse().getMacroHandlers(); | ||||||
|  |             Object obj = macroHandlers.get(handlerName); | ||||||
|  |             if (handlerCache != null && obj != null) { | ||||||
|  |                 handlerCache.put(handlerName, obj); | ||||||
|  |             } | ||||||
|  |             return obj; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Object resolvePath(Object handler, RequestEvaluator reval) { | ||||||
|  |             for (int i = 1; i < path.length - 1; i++) { | ||||||
|  |                 handler = getProperty(handler, path[i], reval); | ||||||
|  |                 if (handler == null) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return handler; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Object getProperty(Object obj, String name, | ||||||
|  |                                    RequestEvaluator reval) { | ||||||
|  |             if (obj instanceof Map) { | ||||||
|  |                 return ((Map) obj).get(name); | ||||||
|  |             } else { | ||||||
|  |                 return reval.getScriptingEngine().getProperty(obj, name); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private boolean hasProperty(Object obj, String name, RequestEvaluator reval) { | ||||||
|  |             if (obj instanceof Map) { | ||||||
|  |                 return ((Map) obj).containsKey(name); | ||||||
|  |             } else { | ||||||
|  |                 return reval.getScriptingEngine().hasProperty(obj, name); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /** |         /** | ||||||
|          * Utility method for writing text out to the response object. |          * Utility method for writing text out to the response object. | ||||||
|          */ |          */ | ||||||
|         void writeResponse(Object value, StringBuffer buffer, |         void writeResponse(Object value, RequestEvaluator reval, | ||||||
|                            RenderParameters renderParams, boolean useDefault) |                            Object thisObject, Map handlerCache, | ||||||
|                 throws UnsupportedEncodingException { |                            StandardParams stdParams, boolean useDefault) | ||||||
|  |                 throws Exception { | ||||||
|             String text; |             String text; | ||||||
|  |             StringBuffer buffer = reval.getResponse().getBuffer(); | ||||||
|  | 
 | ||||||
|  |             // invoke filter chain if defined | ||||||
|  |             if (filterChain != null) { | ||||||
|  |                 value = filterChain.invokeFilter(reval, value, thisObject, handlerCache); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             if (value == null) { |             if (value == null) { | ||||||
|                 if (useDefault) { |                 if (useDefault) { | ||||||
|                     text = renderParams.defaultValue; |                     text = (String) stdParams.defaultValue; | ||||||
|                 } else { |                 } else { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 // do not render doubles as doubles unless |                 text = reval.getScriptingEngine().toString(value); | ||||||
|                 // they actually have a decimal place. This is necessary because |  | ||||||
|                 // all numbers are handled as Double in JavaScript. |  | ||||||
|                 if (value instanceof Double) { |  | ||||||
|                     Double d = (Double) value; |  | ||||||
|                     if (d.longValue() == d.doubleValue()) { |  | ||||||
|                         text = Long.toString(d.longValue()); |  | ||||||
|                     } else { |  | ||||||
|                         text = d.toString(); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     text = value.toString(); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if ((text != null) && (text.length() > 0)) { |             if ((text != null) && (text.length() > 0)) { | ||||||
|                 // only write prefix/suffix if value is not null, if we write the default |                 // only write prefix/suffix if value is not null, if we write the default | ||||||
|                 // value provided by the macro tag, we assume it's already complete |                 // value provided by the macro tag, we assume it's already complete | ||||||
|                 if (renderParams.prefix != null && value != null) { |                 if (stdParams.prefix != null && value != null) { | ||||||
|                     buffer.append(renderParams.prefix); |                     buffer.append(stdParams.prefix); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 switch (encoding) { |                 switch (encoding) { | ||||||
|  | @ -727,36 +852,65 @@ public final class Skin { | ||||||
|                         break; |                         break; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (renderParams.suffix != null && value != null) { |                 if (stdParams.suffix != null && value != null) { | ||||||
|                     buffer.append(renderParams.suffix); |                     buffer.append(stdParams.suffix); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public String toString() { |         public String toString() { | ||||||
|             return "[Macro: " + fullName + "]"; |             return "[Macro: " + name + "]"; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /** |         /** | ||||||
|          * Return the full name of the macro in handler.name notation |          * Return the full name of the macro in handler.name notation | ||||||
|  |          * @return the macro name | ||||||
|          */ |          */ | ||||||
|         public String getFullName() { |         public String getName() { | ||||||
|             return fullName; |             return name; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     class RenderParameters { |     class StandardParams { | ||||||
|         String prefix = null; |         Object prefix = null; | ||||||
|         String suffix = null; |         Object suffix = null; | ||||||
|         String defaultValue = null; |         Object defaultValue = null; | ||||||
|  |         boolean silentFailMode = false; | ||||||
| 
 | 
 | ||||||
|         RenderParameters() { |         StandardParams() {} | ||||||
|  | 
 | ||||||
|  |         StandardParams(Map map) { | ||||||
|  |             prefix = map.get("prefix"); | ||||||
|  |             suffix = map.get("suffix"); | ||||||
|  |             defaultValue = map.get("default"); | ||||||
|  |             silentFailMode = "silent".equals(map.get("failmode")); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         boolean containsMacros() { | ||||||
|  |             return prefix instanceof Macro | ||||||
|  |                 || suffix instanceof Macro | ||||||
|  |                 || defaultValue instanceof Macro; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void setFailMode(Object value) { | ||||||
|  |             if ("silent".equals(value)) | ||||||
|  |                 silentFailMode = true; | ||||||
|  |             else if ("verbose".equals(value)) | ||||||
|  |                 silentFailMode = false; | ||||||
|  |             else | ||||||
|  |                 app.logEvent("unrecognized failmode value: " + value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         StandardParams render(RequestEvaluator reval, Object thisObj, Map handlerCache) | ||||||
|  |                 throws UnsupportedEncodingException { | ||||||
|  |             if (!containsMacros()) | ||||||
|  |                 return this; | ||||||
|  |             StandardParams stdParams = new StandardParams(); | ||||||
|  |             stdParams.prefix = renderMacro(prefix, reval, thisObj, handlerCache); | ||||||
|  |             stdParams.suffix = renderMacro(suffix, reval, thisObj, handlerCache); | ||||||
|  |             stdParams.defaultValue = renderMacro(defaultValue, reval, thisObj, handlerCache); | ||||||
|  |             return stdParams; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         RenderParameters(Map map) { |  | ||||||
|             prefix = (String) map.get("prefix"); |  | ||||||
|             suffix = (String) map.get("suffix"); |  | ||||||
|             defaultValue = (String) map.get("default"); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -109,14 +109,35 @@ public interface ScriptingEngine { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get a property on an object |      * Get a property on an object | ||||||
|  |      * @param thisObject the object | ||||||
|  |      * @param key the property name | ||||||
|  |      * @return true the property value, or null | ||||||
|      */ |      */ | ||||||
|     public Object get(Object thisObject, String key); |     public Object getProperty(Object thisObject, String key); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Return true if a function by that name is defined for that object. |      * Return true if a function by that name is defined for that object. | ||||||
|  |      * @param thisObject the object | ||||||
|  |      * @param functionName the function name | ||||||
|  |      * @return true if the function is defined on the object | ||||||
|      */ |      */ | ||||||
|     public boolean hasFunction(Object thisObject, String functionName); |     public boolean hasFunction(Object thisObject, String functionName); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Return true if a property by that name is defined for that object. | ||||||
|  |      * @param thisObject the object | ||||||
|  |      * @param propertyName the property name | ||||||
|  |      * @return true if the function is defined on the object | ||||||
|  |      */ | ||||||
|  |     public boolean hasProperty(Object thisObject, String propertyName); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return a string representation for the given object | ||||||
|  |      * @param obj an object | ||||||
|  |      * @return a string representing the object | ||||||
|  |      */ | ||||||
|  |     public String toString(Object obj); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  Get an IPathElement that offers introspection services into the application. |      *  Get an IPathElement that offers introspection services into the application. | ||||||
|      *  If this method returns null, no introspection is available for this kind of engine. |      *  If this method returns null, no introspection is available for this kind of engine. | ||||||
|  |  | ||||||
|  | @ -255,7 +255,7 @@ public class RhinoEngine implements ScriptingEngine { | ||||||
|             } |             } | ||||||
|             Object f = ScriptableObject.getProperty(obj, functionName); |             Object f = ScriptableObject.getProperty(obj, functionName); | ||||||
| 
 | 
 | ||||||
|             if ((f == ScriptableObject.NOT_FOUND) || !(f instanceof Function)) { |             if (!(f instanceof Function)) { | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -353,51 +353,69 @@ public class RhinoEngine implements ScriptingEngine { | ||||||
| 
 | 
 | ||||||
|         Object func = ScriptableObject.getProperty(op, fname); |         Object func = ScriptableObject.getProperty(op, fname); | ||||||
| 
 | 
 | ||||||
|         return func instanceof Function; |         return func instanceof Callable; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if an object has a value property defined with that name. | ||||||
|  |      */ | ||||||
|  |     public boolean hasProperty(Object obj, String propname) { | ||||||
|  |         if ((obj == null) || (propname == null)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         String prototypeName = app.getPrototypeName(obj); | ||||||
|  |         if ("user".equalsIgnoreCase(prototypeName) | ||||||
|  |                 && "password".equalsIgnoreCase(propname)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         // if this is a HopObject, check if the property is defined | ||||||
|  |         // in the type.properties db-mapping. | ||||||
|  |         if (obj instanceof INode && ! "hopobject".equalsIgnoreCase(prototypeName)) { | ||||||
|  |             DbMapping dbm = app.getDbMapping(prototypeName); | ||||||
|  |             if (dbm != null) { | ||||||
|  |                 Relation rel = dbm.propertyToRelation(propname); | ||||||
|  |                 return rel != null && (rel.isPrimitive() || rel.isCollection()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Scriptable wrapped = Context.toObject(obj, global); | ||||||
|  |         return wrapped.has(propname, wrapped); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Check if an object has a defined property (public field if it |      * Check if an object has a defined property (public field if it | ||||||
|      * is a java object) with that name. |      * is a java object) with that name. | ||||||
|      */ |      */ | ||||||
|     public Object get(Object obj, String propname) { |     public Object getProperty(Object obj, String propname) { | ||||||
|         if ((obj == null) || (propname == null)) { |         if ((obj == null) || (propname == null)) | ||||||
|             return null; |             return null; | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         String prototypeName = app.getPrototypeName(obj); |  | ||||||
| 
 |  | ||||||
|         if ("user".equalsIgnoreCase(prototypeName) && |  | ||||||
|                 "password".equalsIgnoreCase(propname)) { |  | ||||||
|             return "[macro access to password property not allowed]"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // if this is a HopObject, check if the property is defined |  | ||||||
|         // in the type.properties db-mapping. |  | ||||||
|         if (obj instanceof INode) { |  | ||||||
|             DbMapping dbm = app.getDbMapping(prototypeName); |  | ||||||
| 
 |  | ||||||
|             if (dbm != null) { |  | ||||||
|                 Relation rel = dbm.propertyToRelation(propname); |  | ||||||
| 
 |  | ||||||
|                 if ((rel == null) || !rel.isPrimitive()) { |  | ||||||
|                     return "[property \"" + propname + "\" is not defined for " + |  | ||||||
|                            prototypeName + "]"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         Scriptable so = Context.toObject(obj, global); |         Scriptable so = Context.toObject(obj, global); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             Object prop = so.get(propname, so); |             Object prop = so.get(propname, so); | ||||||
| 
 | 
 | ||||||
|             if ((prop == null) || (prop == Undefined.instance) |             if ((prop == null) | ||||||
|  |                     || (prop == Undefined.instance) | ||||||
| 	                || (prop == ScriptableObject.NOT_FOUND)) { | 	                || (prop == ScriptableObject.NOT_FOUND)) { | ||||||
|                 return null; |                 return null; | ||||||
|             } else if (prop instanceof Wrapper) { |             } else if (prop instanceof Wrapper) { | ||||||
|                 return ((Wrapper) prop).unwrap(); |                 return ((Wrapper) prop).unwrap(); | ||||||
|             } else { |             } else { | ||||||
|  |                 return prop; | ||||||
|  |             } | ||||||
|  |         } catch (Exception esx) { | ||||||
|  |             app.logError("Error getting property " + propname + ": " + esx); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return a string representation for the given object | ||||||
|  |      * @param obj an object | ||||||
|  |      * @return a string representing the object | ||||||
|  |      */ | ||||||
|  |     public String toString(Object obj) { | ||||||
|         // not all Rhino types convert to a string as expected |         // not all Rhino types convert to a string as expected | ||||||
|         // when calling toString() - try to do better by using |         // when calling toString() - try to do better by using | ||||||
|         // Rhino's ScriptRuntime.toString(). Note that this |         // Rhino's ScriptRuntime.toString(). Note that this | ||||||
|  | @ -405,15 +423,11 @@ public class RhinoEngine implements ScriptingEngine { | ||||||
|         // a string representation of the object - which is |         // a string representation of the object - which is | ||||||
|         // currently the case since it's only used in Skin rendering. |         // currently the case since it's only used in Skin rendering. | ||||||
|         try { |         try { | ||||||
|                     return ScriptRuntime.toString(prop); |             return ScriptRuntime.toString(obj); | ||||||
|         } catch (Exception x) { |         } catch (Exception x) { | ||||||
|             // just return original property object |             // just return original property object | ||||||
|         } |         } | ||||||
|                 return prop; |         return obj.toString(); | ||||||
|             } |  | ||||||
|         } catch (Exception esx) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue