Coverage Report - org.webslinger.WebslingerInvoker
 
Classes in this File Line Coverage Branch Coverage Complexity
WebslingerInvoker
78%
133/171
93%
28/30
0
WebslingerInvoker$1
100%
4/4
N/A
0
WebslingerInvoker$2
17%
2/12
N/A
0
WebslingerInvoker$3
33%
1/3
N/A
0
WebslingerInvoker$4
100%
3/3
N/A
0
WebslingerInvoker$AlterPlan
100%
1/1
N/A
0
WebslingerInvoker$ConvertHelper
N/A
N/A
0
WebslingerInvoker$Plan
77%
86/111
75%
21/28
0
WebslingerInvoker$Plan$1
100%
2/2
N/A
0
WebslingerInvoker$Plan$2
100%
2/2
N/A
0
WebslingerInvoker$PlanCreator
100%
1/1
N/A
0
 
 1  
 package org.webslinger;
 2  
 
 3  
 import java.io.ByteArrayInputStream;
 4  
 import java.io.ByteArrayOutputStream;
 5  
 import java.io.IOException;
 6  
 import java.io.InputStream;
 7  
 import java.io.ObjectOutputStream;
 8  
 import java.io.StringWriter;
 9  
 import java.io.Writer;
 10  
 import java.util.ArrayList;
 11  
 import java.util.Enumeration;
 12  
 import java.util.HashMap;
 13  
 import java.util.List;
 14  
 import java.util.Map;
 15  
 import java.util.concurrent.Callable;
 16  
 import java.util.concurrent.Future;
 17  
 import java.util.concurrent.TimeUnit;
 18  
 import java.util.logging.Level;
 19  
 import java.util.logging.Logger;
 20  
 import java.util.logging.LogRecord;
 21  
 import javax.servlet.Filter;
 22  
 import javax.servlet.FilterChain;
 23  
 import javax.servlet.FilterConfig;
 24  
 import javax.servlet.ServletContext;
 25  
 import javax.servlet.ServletContextEvent;
 26  
 import javax.servlet.ServletContextListener;
 27  
 import javax.servlet.ServletException;
 28  
 import javax.servlet.ServletRequest;
 29  
 import javax.servlet.ServletRequestEvent;
 30  
 import javax.servlet.ServletRequestListener;
 31  
 import javax.servlet.ServletResponse;
 32  
 import javax.servlet.http.HttpServletRequest;
 33  
 import javax.servlet.http.HttpServletResponse;
 34  
 import javax.servlet.http.HttpSession;
 35  
 
 36  
 import org.apache.bsf.BSFException;
 37  
 
 38  
 import org.webslinger.commons.vfs.FileConvertor;
 39  
 import org.webslinger.commons.vfs.VFSUtil;
 40  
 import org.webslinger.container.WebslingerContainer;
 41  
 import org.webslinger.servlet.FakeHttpServletRequest;
 42  
 import org.webslinger.servlet.FakeHttpServletResponse;
 43  
 import org.webslinger.servlet.FakeServletRequest;
 44  
 import org.webslinger.servlet.FakeServletResponse;
 45  
 import org.webslinger.io.Charsets;
 46  
 import org.webslinger.io.IOUtil;
 47  
 import org.webslinger.lang.ExecutionPool;
 48  
 import org.webslinger.lang.ThreadSingletonState;
 49  
 import org.webslinger.util.TTLObject;
 50  
 import org.webslinger.rules.Action;
 51  
 import org.webslinger.rules.AbstractListValue;
 52  
 import org.webslinger.rules.AbstractSingleValue;
 53  
 import org.webslinger.rules.AbstractValue;
 54  
 import org.webslinger.rules.CompiledRules;
 55  
 import org.webslinger.rules.CSSAction;
 56  
 import org.webslinger.rules.CSSRules;
 57  
 import org.webslinger.rules.CSSSelector;
 58  
 import org.webslinger.rules.Rules;
 59  
 import org.webslinger.servlet.webxml.FilterMapping;
 60  
 import org.webslinger.vfs.VFSDelegate;
 61  
 
 62  414
 public class WebslingerInvoker {
 63  1
     private static final Logger logger = Logger.getLogger(WebslingerInvoker.class.getName());
 64  1
     private static ThreadLocal<WebslingerInvoker> invokerLocal = new ThreadLocal<WebslingerInvoker>();
 65  
 
 66  
     public WebslingerContainer getContainer() {
 67  0
         return webslingerServletContext.getContainer();
 68  
     }
 69  
 
 70  
     public WebslingerServletContext getWebslingerServletContext() {
 71  496
         return webslingerServletContext;
 72  
     }
 73  
 
 74  373
     protected final ArrayList<Webslinger> webslingers = new ArrayList<Webslinger>();
 75  
     protected final CompiledRules compiledRules;
 76  
     protected final WebslingerServletContext webslingerServletContext;
 77  
     protected final VFSDelegate<?, Object, ?> vfsDelegate;
 78  373
     protected final ThreadSingletonState threadStateInitializer = new ThreadSingletonState() {
 79  
         public void start() {
 80  373
         }
 81  
 
 82  
         public void finish() {
 83  373
             WebslingerInvoker.this.reset();
 84  373
         }
 85  
     };
 86  
     protected final ArrayList<Map<String, Object>> jsonHub;
 87  
 
 88  
     protected Webslinger current;
 89  
     protected boolean wrapperFound;
 90  
     protected String themeName;
 91  
     protected String themePath;
 92  
     protected Plan plan;
 93  
 
 94  308
     WebslingerInvoker(WebslingerServletContext webslingerServletContext) throws IOException {
 95  308
         this.webslingerServletContext = webslingerServletContext;
 96  308
         compiledRules = webslingerServletContext.getContainer().getRules();
 97  308
         vfsDelegate = webslingerServletContext.getVFSDelegate();
 98  308
         ExecutionPool.addAtExit(threadStateInitializer);
 99  308
         jsonHub = new ArrayList<Map<String, Object>>();
 100  308
     }
 101  
 
 102  65
     private WebslingerInvoker(WebslingerInvoker parent) throws IOException {
 103  65
         webslingers.addAll(parent.webslingers);
 104  65
         compiledRules = parent.compiledRules;
 105  65
         webslingerServletContext = parent.webslingerServletContext;
 106  65
         vfsDelegate = parent.vfsDelegate;
 107  65
         wrapperFound = parent.wrapperFound;
 108  65
         themeName = parent.themeName;
 109  65
         themePath = parent.themePath;
 110  65
         ExecutionPool.addAtExit(threadStateInitializer);
 111  65
         jsonHub = parent.jsonHub;
 112  65
     }
 113  
 
 114  
     public static WebslingerInvoker getGlobalInvoker() throws IOException {
 115  0
         return invokerLocal.get();
 116  
     }
 117  
 
 118  
     public String getThemePath() {
 119  3
         return themePath;
 120  
     }
 121  
 
 122  
     void sendJSONEvent(String topic, Object data) {
 123  0
         Map<String, Object> msg = new HashMap<String, Object>(2);
 124  0
         msg.put("topic", topic);
 125  0
         msg.put("data", data);
 126  0
         jsonHub.add(msg);
 127  0
     }
 128  
 
 129  
     List<Map<String, Object>> getJSONHub() {
 130  0
         return jsonHub;
 131  
     }
 132  
 
 133  
     public void reset() {
 134  680
         themeName = null;
 135  680
         themePath = null;
 136  680
         wrapperFound = false;
 137  680
     }
 138  
 
 139  
     public Map<String, Object> findContext() {
 140  
         // Don't need to check stackPtr > 0, as we are *always* called
 141  
         // from another resource
 142  99
         return current == null ? null : current.getContext();
 143  
     }
 144  
 
 145  
     public String getText(PathContext pc) throws IOException, ServletException {
 146  0
         return VFSUtil.getString(pc.getFile());
 147  
     }
 148  
 
 149  
     public Future runAsync(long delay, final PathContext pc, final Map<String, Object> context, final String command, final Object payload) throws IOException, ServletException {
 150  
         // top-level invocation
 151  
         if (false) {
 152  
         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 153  
         try {
 154  
             new ObjectOutputStream(bytes).writeObject(context);
 155  
         } catch (IOException e) {
 156  
             LogRecord logRecord = new LogRecord(Level.WARNING, "Couldn't serialize object, for possible saving to disk");
 157  
             //logRecord.setThrown(e);
 158  
             logger.log(logRecord);
 159  
         }
 160  
         }
 161  65
         final HttpServletResponse response = new FakeHttpServletResponse(new FakeServletResponse(new StringWriter()));
 162  65
         final HttpServletRequest request = new FakeHttpServletRequest(webslingerServletContext, new FakeServletRequest(webslingerServletContext, pc), response, pc);
 163  65
         final WebslingerInvoker sub = new WebslingerInvoker(this);
 164  65
         return ExecutionPool.schedule(
 165  65
             new Callable<Object>() {
 166  
                 public Object call() throws Exception {
 167  
                     try {
 168  65
                         return sub.invoke(pc, null, request, response, context, command, payload);
 169  0
                     } catch (Exception e) {
 170  0
                         LogRecord logRecord = new LogRecord(Level.SEVERE, "Exception during async call");
 171  0
                         logRecord.setThrown(e);
 172  0
                         logger.log(logRecord);
 173  0
                         throw e;
 174  0
                     } catch (Error e) {
 175  0
                         LogRecord logRecord = new LogRecord(Level.SEVERE, "Error during async call");
 176  0
                         logRecord.setThrown(e);
 177  0
                         logger.log(logRecord);
 178  0
                         throw e;
 179  
                     }
 180  
                 }
 181  
             },
 182  
             delay,
 183  
             TimeUnit.MILLISECONDS
 184  
         );
 185  
     }
 186  
 
 187  
     private Webslinger captureInit(PathContext pc, HttpServletRequest request, HttpServletResponse response, Writer writer, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 188  0
         response = new FakeHttpServletResponse(new FakeServletResponse(writer));
 189  0
         request = new FakeHttpServletRequest(webslingerServletContext, new FakeServletRequest(webslingerServletContext, pc), response, pc);
 190  0
         return initRequest(newWebslinger(Action.CAPTURE, pc, request, response, context, command, payload));
 191  
     }
 192  
 
 193  
     public void capture(PathContext pc, PathContext template, HttpServletRequest request, HttpServletResponse response, Writer writer, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 194  0
         boolean oldWrapperFound = wrapperFound;
 195  0
         wrapperFound = false;
 196  
         try {
 197  0
             invoke(captureInit(pc, request, response, writer, context, command, payload), template).run();
 198  
         } finally {
 199  0
             wrapperFound = oldWrapperFound;
 200  0
         }
 201  0
     }
 202  
 
 203  
     public void capture(PathContext pc, HttpServletRequest request, HttpServletResponse response, Writer writer, Map<String, Object> context, String command, Object payload, PathContext... templates) throws IOException, ServletException {
 204  0
         boolean oldWrapperFound = wrapperFound;
 205  0
         wrapperFound = false;
 206  
         try {
 207  0
             invoke(captureInit(pc, request, response, writer, context, command, payload), templates).run();
 208  
         } finally {
 209  0
             wrapperFound = oldWrapperFound;
 210  0
         }
 211  0
     }
 212  
 
 213  
     Object runDirect(PathContext pc, HttpServletRequest request, HttpServletResponse response, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 214  83
         if (logger.isLoggable(Level.FINE)) logger.fine("runDirect(" + pc + ")");
 215  83
         Webslinger webslinger = newWebslinger(Action.CONTENT, pc, request, response, context, command, payload);
 216  83
         initRequest(webslinger);
 217  83
         pushFrame(webslinger);
 218  
         try {
 219  83
             return invokeContent(webslinger);
 220  
         } finally {
 221  83
             popFrame();
 222  
         }
 223  
     }
 224  
 
 225  
     private Webslinger newWebslinger(Action action, PathContext pc, Map<String, Object> context, String command, Object payload, Writer writer) throws IOException, ServletException {
 226  565
         if (logger.isLoggable(Level.FINE)) logger.fine("runDirect(" + pc + ")");
 227  
         HttpServletRequest request;
 228  
         HttpServletResponse response;
 229  565
         if (current == null) {
 230  461
             response = new FakeHttpServletResponse(new FakeServletResponse(writer));
 231  461
             request = new FakeHttpServletRequest(webslingerServletContext, new FakeServletRequest(webslingerServletContext, pc), response, pc);
 232  
         } else {
 233  104
             request = getWebslingerServletContext().alterPaths(pc, current.getRequest());
 234  104
             response = getWebslingerServletContext().alterPaths(pc, request, current.getResponse(), writer);
 235  
         }
 236  565
         return newWebslinger(action, pc, request, response, context, command, payload);
 237  
     }
 238  
 
 239  
     Object runDirect(PathContext pc, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 240  461
         Webslinger webslinger = newWebslinger(Action.CONTENT, pc, context, command, payload, new StringWriter());
 241  461
         initRequest(webslinger);
 242  461
         pushFrame(webslinger);
 243  
         try {
 244  461
             return invokeContent(webslinger);
 245  
         } finally {
 246  461
             popFrame();
 247  
         }
 248  
     }
 249  
 
 250  
     public Webslinger initRequest(PathContext pc, Map<String, Object> context, String command, Object payload, Writer writer) throws IOException, ServletException {
 251  104
         Webslinger webslinger = newWebslinger(Action.CONTENT, pc, context, command, payload, writer);
 252  104
         initRequest(webslinger);
 253  104
         if (logger.isLoggable(Level.FINE)) logger.fine("invoke(" + pc + ")");
 254  104
         return webslinger;
 255  
     }
 256  
 
 257  
     public Object run(PathContext pc, PathContext template, Map<String, Object> context, String command, Object payload, Writer writer) throws IOException, ServletException {
 258  104
         return invoke(initRequest(pc, context, command, payload, writer), template).run();
 259  
     }
 260  
 
 261  
     public Object run(PathContext pc, Map<String, Object> context, String command, Object payload, Writer writer, PathContext... templates) throws IOException, ServletException {
 262  0
         return invoke(initRequest(pc, context, command, payload, writer), templates).run();
 263  
     }
 264  
 
 265  
     protected CSSAction[] findActions(Webslinger current, Action action, boolean all) throws IOException, ServletException {
 266  
         // FIXME: use specificity
 267  3265
         return compiledRules != null ? compiledRules.findActions(action, current.getRequest(), current, all) : new CSSAction[0];
 268  
     }
 269  
 
 270  
     protected CSSAction[] findActions(ServletRequest request, Action action, boolean all) throws IOException, ServletException {
 271  
         // FIXME: use specificity
 272  281
         return compiledRules != null ? compiledRules.findActions(action, request, current, all) : new CSSAction[0];
 273  
     }
 274  
 
 275  
     protected void showActions(ServletRequest request, CSSAction[] actions) throws IOException, ServletException {
 276  0
         CompiledRules.showActions(actions, request, current);
 277  0
     }
 278  
 
 279  
     protected Object getFirstValue(ServletRequest request, CSSAction[] actions) throws IOException, ServletException {
 280  196
         return CompiledRules.getFirstValue(actions, request, current);
 281  
     }
 282  
 
 283  
     protected <T> T convertTo(ConvertHelper<T> helper, ServletRequest request, Object id) throws IOException {
 284  
         try {
 285  281
             CSSAction[] actions = findActions(request, Action.TRANSFORM, false);
 286  281
             if (actions == null) return helper.noConvert(id);
 287  196
             String convertorName = (String) getFirstValue(request, actions);
 288  196
             if (convertorName == null) return helper.noConvert(id);
 289  3
             FileConvertor convertor = FileConvertor.getConvertor(convertorName);
 290  3
             if (convertor == null) return helper.noConvert(id);
 291  3
             String data = vfsDelegate.getString(id);
 292  3
             return helper.convert(convertor.convert(id, id.toString(), vfsDelegate.getString(id), null));
 293  0
         } catch (ServletException e) {
 294  0
             throw (IOException) new IOException(e.getMessage()).initCause(e);
 295  
         }
 296  
     }
 297  
 
 298  
     protected interface ConvertHelper<T> {
 299  
         T noConvert(Object id) throws IOException;
 300  
         T convert(String data) throws IOException;
 301  
     }
 302  
 
 303  373
     private final ConvertHelper<InputStream> StreamConvert = new ConvertHelper<InputStream>() {
 304  
         public InputStream noConvert(Object id) throws IOException {
 305  0
             return vfsDelegate.openInput(id);
 306  
         }
 307  
 
 308  
         public InputStream convert(String data) throws IOException {
 309  0
             return new ByteArrayInputStream(IOUtil.getBytes(data, Charsets.UTF8));
 310  
         }
 311  
     };
 312  
 
 313  654
     private final ConvertHelper<String> StringConvert = new ConvertHelper<String>() {
 314  
         public String noConvert(Object id) throws IOException {
 315  278
             return vfsDelegate.getString(id);
 316  
         }
 317  
 
 318  
         public String convert(String data) throws IOException {
 319  3
             return data;
 320  
         }
 321  
     };
 322  
 
 323  
     public InputStream convertToStream(ServletRequest request, Object id) throws IOException {
 324  0
         return convertTo(StreamConvert, request, id);
 325  
     }
 326  
 
 327  
     public String convertToString(ServletRequest request, Object id) throws IOException {
 328  281
         return convertTo(StringConvert, request, id);
 329  
     }
 330  
 
 331  
     public void pushFrame(Webslinger webslinger) {
 332  1637
         webslinger.setParent(current);
 333  1633
         current = webslinger;
 334  1630
     }
 335  
 
 336  
     public Webslinger popFrame() {
 337  
         try {
 338  1632
             return current;
 339  
         } finally {
 340  1637
             current = current.getParent();
 341  
         }
 342  
     }
 343  
 
 344  
     public Webslinger peekFrame() {
 345  0
         return current;
 346  
     }
 347  
 
 348  
     private Webslinger newWebslinger(Action type, Webslinger base, PathContext pc) throws IOException, ServletException {
 349  868
         if (pc == null) return null;
 350  126
         return setCaller(new Webslinger(type, base, pc));
 351  
     }
 352  
 
 353  
     private Webslinger[] newWebslinger(Action type, Webslinger base, PathContext... pc) throws IOException, ServletException {
 354  0
         List<Webslinger> webslingers = new ArrayList<Webslinger>(pc.length);
 355  0
         for (int i = 0; i < pc.length; i++) {
 356  0
             if (pc[i] == null) continue;
 357  0
             webslingers.add(setCaller(new Webslinger(type, base, pc[i])));
 358  
         }
 359  0
         return webslingers.toArray(new Webslinger[webslingers.size()]);
 360  
     }
 361  
 
 362  
     private Webslinger newWebslinger(Action type, PathContext pc, HttpServletRequest request, HttpServletResponse response, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 363  1432
         return setCaller(new Webslinger(type, webslingerServletContext, this, pc, request, response, context, command, payload));
 364  
     }
 365  
 
 366  
     private Webslinger newWebslinger(Action type, Webslinger base, PathContext pc, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 367  40
         return setCaller(new Webslinger(type, webslingerServletContext, this, pc, base.getRequest(), base.getResponse(), context, command, payload));
 368  
     }
 369  
 
 370  
     private Webslinger setCaller(Webslinger webslinger) {
 371  1590
         if (current != null) webslinger.setCaller(current);
 372  1595
         return webslinger;
 373  
     }
 374  
 
 375  
     protected Webslinger initRequest(Webslinger webslinger) throws IOException {
 376  1281
         HttpServletRequest request = webslinger.getRequest();
 377  1282
         HttpServletResponse response = webslinger.getResponse();
 378  1284
         PathContext pc = webslinger.getPathContext();
 379  1286
         if (themeName == null) {
 380  307
             if (request instanceof HttpServletRequest) {
 381  307
                 HttpSession session = ((HttpServletRequest) request).getSession(false);
 382  307
                 if (session != null) {
 383  6
                     themeName = (String) session.getAttribute("THEME");
 384  
                 } else {
 385  301
                     themeName = null;
 386  
                 }
 387  307
             } else {
 388  0
                 themeName = null;
 389  
             }
 390  307
             if (themeName == null) themeName = compiledRules != null ? compiledRules.rules.getDefaultTheme() : "Default";
 391  307
             themePath = "/Theme/" + themeName;
 392  
         }
 393  1283
         request.setAttribute("THEME", themeName);
 394  1282
         request.setAttribute("THEMEPATH", themePath);
 395  1283
         String contentType = (String) pc.getAttribute("content-type");
 396  1288
         String charset = (String) pc.getAttribute("charset");
 397  1287
         if (contentType != null) response.setContentType(contentType);
 398  1288
         if (charset != null) response.setCharacterEncoding(charset);
 399  1288
         webslinger.getContext().put("ThemePath", themePath);
 400  1285
         return webslinger;
 401  
     }
 402  
 
 403  
     public Webslinger initRequest(PathContext pc, HttpServletRequest request, HttpServletResponse response, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 404  640
         Webslinger webslinger = newWebslinger(Action.CONTENT, pc, request, response, context, command, payload);
 405  633
         initRequest(webslinger);
 406  637
         return webslinger;
 407  
     }
 408  
 
 409  
     public Object invoke(final PathContext pc, final PathContext template, final HttpServletRequest request, final HttpServletResponse response, Map<String, Object> context, String command, Object payload) throws IOException, ServletException {
 410  640
         if (logger.isLoggable(Level.FINE)) logger.fine("invoke(" + pc + ")");
 411  640
         return invoke(initRequest(pc, request, response, context, command, payload), template).run();
 412  
     }
 413  
 
 414  
     private Plan invoke(Webslinger webslinger, PathContext template) throws IOException, ServletException {
 415  739
         return new Plan(webslinger).addTemplates(webslinger).addTemplates(newWebslinger(Action.TEMPLATE, webslinger, template)).checkForWrapper(webslinger);
 416  
     }
 417  
 
 418  
     private Plan invoke(Webslinger webslinger, PathContext... templates) throws IOException, ServletException {
 419  0
         return new Plan(webslinger).addTemplates(webslinger).addTemplates(newWebslinger(Action.TEMPLATE, webslinger, templates)).checkForWrapper(webslinger);
 420  
     }
 421  
 
 422  
     public Plan getPlan() {
 423  274
         return plan;
 424  
     }
 425  
 
 426  
     protected void runTriggerValue(Webslinger webslinger, Object triggerValue) throws IOException, ServletException {
 427  40
         if (logger.isLoggable(Level.FINE)) logger.fine("runTriggerValue(" + webslinger + ", " + triggerValue);
 428  40
         PathContext trigger = triggerValue instanceof PathContext ? (PathContext) triggerValue : webslinger.resolvePath((String) triggerValue);
 429  40
         Webslinger triggerWebslinger = newWebslinger(Action.TRIGGER, webslinger, trigger, webslinger.getContext(), "trigger", webslinger);
 430  40
         new Plan(webslinger).addTemplates(triggerWebslinger).run();
 431  40
     }
 432  
 
 433  
     protected Object invokeContent(final Webslinger webslinger) throws IOException, ServletException {
 434  1590
         if (logger.isLoggable(Level.FINE)) logger.fine("invokeContent(" + webslinger + ")");
 435  1591
         CSSAction[] triggerActions = findActions(webslinger, Action.TRIGGER, true);
 436  1597
         if (triggerActions != null) {
 437  1593
             ServletRequest request = webslinger.getRequest();
 438  3218
             for (CSSAction action: triggerActions) {
 439  1622
                 AbstractValue value = action.getValue();
 440  1622
                 if (value instanceof AbstractListValue) {
 441  1604
                     Object[] triggerValues = ((AbstractListValue) value).getValue(request, webslinger);
 442  1608
                     if (triggerValues != null) {
 443  42
                         for (Object v: triggerValues) {
 444  21
                             runTriggerValue(webslinger, v);
 445  
                         }
 446  
                     }
 447  1608
                 } else {
 448  19
                     Object triggerValue = ((AbstractSingleValue) value).getValue(request, webslinger);
 449  19
                     if (triggerValue != null) runTriggerValue(webslinger, triggerValue);
 450  
                 }
 451  
             }
 452  
         }
 453  1591
         WebslingerInvoker old = invokerLocal.get();
 454  1598
         invokerLocal.set(this);
 455  
         try {
 456  1598
             return webslinger.getTypeHandler().run(webslinger);
 457  
         } finally {
 458  1592
             invokerLocal.set(old);
 459  
         }
 460  
     }
 461  
 
 462  373
     private final StringBuilder prefix = new StringBuilder();
 463  
 
 464  
     public class Plan {
 465  
         protected final Webslinger reason;
 466  
         protected final Plan parent;
 467  924
         protected final List<Webslinger> plan = new ArrayList<Webslinger>();
 468  
 
 469  
         protected int index;
 470  
 
 471  844
         protected Plan(Webslinger reason) {
 472  848
             this.parent = WebslingerInvoker.this.plan;
 473  843
             this.reason = reason;
 474  843
         }
 475  
 
 476  77
         protected Plan(Plan reason) {
 477  77
             this.parent = WebslingerInvoker.this.plan;
 478  77
             this.reason = reason.reason;
 479  77
             plan.addAll(reason.plan.subList(0, reason.index));
 480  77
             index = plan.size();
 481  77
         }
 482  
 
 483  
         public Plan add(Webslinger webslinger) {
 484  0
             plan.add(index++, webslinger);
 485  0
             return this;
 486  
         }
 487  
 
 488  
         public Plan addTemplates(PathContext template) throws IOException, ServletException {
 489  0
             if (template == null) return this;
 490  0
             if (index == 0) throw new InternalError("plan is empty");
 491  0
             return addTemplates(newWebslinger(Action.TEMPLATE, plan.get(index - 1), template));
 492  
         }
 493  
 
 494  
         public Plan addTemplates(PathContext... templates) throws IOException, ServletException {
 495  270
             if (templates == null || templates.length == 0) return this;
 496  0
             if (index == 0) throw new InternalError("plan is empty");
 497  0
             for (int i = 0; i < templates.length; i++) {
 498  0
                 addTemplates(newWebslinger(Action.TEMPLATE, plan.get(index - 1), templates[i]));
 499  
             }
 500  0
             return this;
 501  
         }
 502  
 
 503  
         public Plan addTemplates(Webslinger ptr) throws IOException, ServletException {
 504  1757
             return addAll(ptr, Action.TEMPLATE);
 505  
         }
 506  
 
 507  
         private final Plan addAll(Webslinger ptr, Action action) throws IOException, ServletException {
 508  1788
             if (ptr == null) return this;
 509  1048
             plan.add(index++, ptr);
 510  1048
             CSSAction[] actions = findActions(ptr, action, false);
 511  1047
             if (actions == null) return this;
 512  825
             for (int i = 0; i < actions.length; i++) {
 513  405
                 AbstractValue value = actions[i].getValue();
 514  405
                 if (value == null) break;
 515  405
                 if (value instanceof AbstractListValue) {
 516  405
                     Object[] values = ((AbstractListValue) value).getValue(ptr.getRequest(), ptr);
 517  405
                     if (values != null) {
 518  64
                         for (Object v: values) {
 519  32
                             PathContext template = v instanceof PathContext ? (PathContext) v : ptr.resolvePath((String) v);
 520  32
                             addAll(newWebslinger(action, ptr, template), action);
 521  
                         }
 522  
                     }
 523  405
                 } else {
 524  0
                     Object v = ((AbstractSingleValue) value).getValue(ptr.getRequest(), ptr);
 525  0
                     if (v != null) {
 526  0
                         PathContext template = v instanceof PathContext ? (PathContext) v : ptr.resolvePath((String) v);
 527  0
                         addAll(newWebslinger(action, ptr, template), action);
 528  
                     }
 529  
                 }
 530  
             }
 531  420
             return this;
 532  
         }
 533  
 
 534  
         public Plan addTemplates(Webslinger... templates) throws IOException, ServletException {
 535  0
             for (int i = 0; i < templates.length; i++) {
 536  0
                 Webslinger ptr = templates[i];
 537  0
                 addAll(ptr, Action.TEMPLATE);
 538  
             }
 539  0
             return this;
 540  
         }
 541  
 
 542  
         public Plan checkForWrapper(Webslinger webslinger) throws IOException, ServletException {
 543  738
             if (!wrapperFound) {
 544  623
                 CSSAction[] actions = findActions(webslinger, Action.WRAP, false);
 545  626
                 if (actions != null) {
 546  106
                     wrapperFound = true;
 547  106
                     PathContext pc = (PathContext) CompiledRules.getFirstValue(actions, webslinger.getRequest(), webslinger);
 548  106
                     if (pc != null) return addTemplates(newWebslinger(Action.WRAP, webslinger, pc));
 549  
                 }
 550  
             }
 551  642
             return this;
 552  
         }
 553  
 
 554  
         public Object run() throws IOException, ServletException {
 555  923
             if (index == 0) throw new InternalError("plan is empty");
 556  920
             index--;
 557  926
             Plan oldPlan = WebslingerInvoker.this.plan;
 558  
             try {
 559  926
                 WebslingerInvoker.this.plan = this;
 560  924
                 Webslinger webslinger = plan.get(index);
 561  927
                 pushFrame(webslinger);
 562  
                 try {
 563  924
                     return invokeContent(webslinger);
 564  
                 } finally {
 565  923
                     popFrame();
 566  
                 }
 567  
             } finally {
 568  921
                 index++;
 569  921
                 WebslingerInvoker.this.plan = oldPlan;
 570  
             }
 571  
         }
 572  
 
 573  
         public Object run(Writer writer) throws IOException, ServletException {
 574  126
             if (index == 0) throw new InternalError("plan is empty");
 575  126
             index--;
 576  
             try {
 577  126
                 Webslinger webslinger = plan.get(index);
 578  126
                 pushFrame(webslinger);
 579  
                 try {
 580  126
                     HttpServletResponse oldResponse = webslinger.pushResponse(writer);
 581  
                     try {
 582  126
                         return invokeContent(webslinger);
 583  
                     } finally {
 584  126
                         webslinger.popResponse(oldResponse);
 585  
                     }
 586  
                 } finally {
 587  126
                     popFrame();
 588  
                 }
 589  
             } finally {
 590  126
                 index++;
 591  
             }
 592  
         }
 593  
 
 594  
         public PathContext findSection(String name) throws IOException {
 595  81
             int i = index;
 596  81
             Webslinger sectionWebslinger = null;
 597  81
             String foundSection = null;
 598  243
             while (i >= 0) {
 599  162
                 Webslinger webslinger = plan.get(i);
 600  162
                 String tmpSection = (String) webslinger.getFileAttribute("section-" + name);
 601  162
                 if (tmpSection != null) {
 602  77
                     foundSection = tmpSection;
 603  77
                     sectionWebslinger = webslinger;
 604  
                 }
 605  162
                 i--;
 606  162
             }
 607  81
             return foundSection == null ? null : sectionWebslinger.resolvePath(foundSection);
 608  
         }
 609  
 
 610  
         public void merge(Plan reason, PathContext pc, Map<String, Object> context, Writer writer, AlterPlan alterPlan) throws IOException {
 611  77
             PlanCreator creator = new PlanCreator<Plan>() {
 612  
                 protected Plan createPlan(Plan reason) {
 613  77
                     return new Plan(reason);
 614  
                 }
 615  
             };
 616  77
             merge(creator, reason, pc, context, writer, alterPlan);
 617  77
         }
 618  
 
 619  
         public void merge(Webslinger reason, PathContext pc, Map<String, Object> context, Writer writer, AlterPlan alterPlan) throws IOException {
 620  67
             PlanCreator creator = new PlanCreator<Webslinger>() {
 621  
                 protected Plan createPlan(Webslinger reason) {
 622  67
                     return new Plan(reason);
 623  
                 }
 624  
             };
 625  67
             merge(creator, reason, pc, context, writer, alterPlan);
 626  67
         }
 627  
 
 628  
         protected <T> void merge(PlanCreator<T> creator, T reason, PathContext pc, Map<String, Object> context, Writer writer, AlterPlan alterPlan) throws IOException {
 629  144
             if (context == null) context = findContext();
 630  
             HttpServletRequest request;
 631  
             HttpServletResponse response;
 632  144
             if (current == null) {
 633  0
                 response = new FakeHttpServletResponse(new FakeServletResponse(writer));
 634  0
                 request = new FakeHttpServletRequest(webslingerServletContext, new FakeServletRequest(webslingerServletContext, pc), response, pc);
 635  
             } else {
 636  144
                 request = getWebslingerServletContext().alterPaths(pc, current.getRequest());
 637  144
                 response = getWebslingerServletContext().alterPaths(pc, request, current.getResponse(), writer);
 638  
             }
 639  144
             if (logger.isLoggable(Level.FINE)) logger.fine("invoke(" + pc + ")");
 640  
             try {
 641  144
                 Webslinger webslinger = newWebslinger(Action.CONTENT, pc, request, response, context, "merge", null);
 642  144
                 Plan oldPlan = WebslingerInvoker.this.plan;
 643  
                 try {
 644  144
                     WebslingerInvoker.this.plan = creator.createPlan(reason).addTemplates(webslinger);
 645  144
                     if (alterPlan != null) alterPlan.alterPlan(WebslingerInvoker.this.plan);
 646  144
                     WebslingerInvoker.this.plan.run();
 647  
                 } finally {
 648  144
                     WebslingerInvoker.this.plan = oldPlan;
 649  144
                 }
 650  0
             } catch (ServletException e) {
 651  0
                 throw (IOException) new IOException(e.getMessage()).initCause(e);
 652  144
             }
 653  144
         }
 654  
 
 655  
         public String toString() {
 656  0
             return "Plan(" + index + ":" + plan + ")";
 657  
         }
 658  
 
 659  
         public Webslinger getReason() {
 660  0
             return reason;
 661  
         }
 662  
 
 663  
         public Webslinger getCurrentWebslinger() {
 664  0
             return plan.get(index);
 665  
         }
 666  
 
 667  
         public Webslinger getNextWebslinger() {
 668  0
             return plan.get(index - 1);
 669  
         }
 670  
     }
 671  
 
 672  144
     public abstract static class AlterPlan {
 673  
         protected abstract void alterPlan(Plan plan) throws IOException, ServletException;
 674  
     }
 675  
 
 676  144
     protected abstract static class PlanCreator<T> {
 677  
         protected abstract Plan createPlan(T reason);
 678  
     }
 679  
 }