001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.framework.basic;
011:
012: import org.mmbase.framework.*;
013: import java.util.*;
014: import javax.servlet.ServletRequest;
015:
016: import javax.servlet.jsp.jstl.core.Config;
017: import javax.servlet.jsp.jstl.fmt.LocalizationContext;
018:
019: import org.mmbase.util.functions.*;
020: import org.mmbase.util.logging.Logger;
021: import org.mmbase.util.logging.Logging;
022:
023: /**
024: * State is a wrapper arround HttpServletRequest which maintains the current state of framework
025: * component rendering.
026: *
027: *
028: * @author Michiel Meeuwissen
029: * @version $Id: State.java,v 1.4 2008/02/22 14:05:57 michiel Exp $
030: * @since MMBase-1.9
031: */
032: public class State {
033:
034: private static final Logger log = Logging
035: .getLoggerInstance(State.class);
036:
037: public final static String KEY = "org.mmbase.framework.state";
038:
039: /**
040: * Returns the framework 'State' object for the given request.
041: * @return a State. Never <code>null</code>.
042: */
043: public static State getState(ServletRequest request) {
044: State state = (State) request.getAttribute(KEY);
045: if (state == null) {
046: state = new State(request);
047: }
048: return state;
049: }
050:
051: private int count = 1;
052: private String id = "";
053: private final int depth;
054: private Renderer renderer = null;
055: private Renderer.Type type = Renderer.Type.NOT;
056: private Processor processor = null;
057: private Parameters frameworkParameters = null;
058: private final ServletRequest request;
059: private final State previousState;
060: private Object originalLocalizationContext = null;
061: private String action = null;
062:
063: /**
064: * Use this constructor, if you want to explicitely create a new State object. E.g. when
065: * starting a <em>sub</com>component.
066: * <code>
067: * state = getState(req);
068: * if (state.isRendering()) {
069: * state = new State(req);
070: * }
071: * </code>
072: * But this is only used by code which want to initiate a new component itself. Normally {@link
073: * #getState(ServletRequest)} should suffice.
074: */
075: public State(ServletRequest r) {
076: request = r;
077: previousState = (State) r.getAttribute(KEY);
078: depth = previousState != null ? previousState.getDepth() + 1
079: : 0;
080: request.setAttribute(KEY, this );
081: }
082:
083: /**
084: * The current window state of rendering. As yet unimplemented.
085: * @todo
086: */
087: public Renderer.WindowState getWindowState() {
088: return Renderer.WindowState.NORMAL;
089: }
090:
091: /**
092: * With recursive includes of blocks, it may occur that the state is only for components inside
093: * a certain other component's block. In that case the depth > 0.
094: */
095: public int getDepth() {
096: return depth;
097: }
098:
099: public ServletRequest getRequest() {
100: return request;
101: }
102:
103: /**
104: * At the end of the request (or subcomponent), this method must be called, to indicate that
105: * this state is no longer in use.
106: */
107: public void end() {
108: request.setAttribute(KEY, previousState);
109: count = 0;
110: id = "";
111: }
112:
113: /**
114: * After rendering (a certain renderer of) a block, the state must be informed about that.
115: */
116: public void endBlock() {
117: renderer = null;
118: processor = null;
119: frameworkParameters = null;
120: request.setAttribute(Config.FMT_LOCALIZATION_CONTEXT
121: + ".request", originalLocalizationContext);
122: }
123:
124: /**
125: * Whether something is rendered right now.
126: */
127: public boolean isRendering() {
128: return renderer != null || processor != null;
129: }
130:
131: /**
132: * Returns the parameters which were used to initialize rendering of the current block.
133: * Or <code>null</code> if currently this state is not 'rendering'.
134: */
135: public Parameters getFrameworkParameters() {
136: return frameworkParameters;
137: }
138:
139: /**
140: * The currently rendered block, or <code>null</code> if not rendering.
141: */
142: public Block getBlock() {
143: return renderer != null ? renderer.getBlock()
144: : (processor != null ? processor.getBlock() : null);
145: }
146:
147: /**
148: * Sets up a LocalizationContext attributes based on {@link Component#getBundle()}and puts in on
149: * the request. This is recognized by fmt:message-tag, which in a JspRenderer can therefore be
150: * used without fmt:bundle or fmt:setbundle.
151: */
152: protected void localizeContext() {
153: String b = getBlock().getComponent().getBundle();
154: if (b != null) {
155: Locale locale = (Locale) request
156: .getAttribute("javax.servlet.jsp.jstl.fmt.locale.request");
157: if (locale == null)
158: org.mmbase.module.core.MMBase.getMMBase().getLocale();
159: originalLocalizationContext = request
160: .getAttribute(Config.FMT_LOCALIZATION_CONTEXT
161: + ".request");
162: request.setAttribute(Config.FMT_LOCALIZATION_CONTEXT
163: + ".request", new LocalizationContext(
164: ResourceBundle.getBundle(b, locale), Locale
165: .getDefault()));
166: }
167: }
168:
169: protected void setType(Renderer.Type t) {
170: if (t.ordinal() < type.ordinal()) {
171: throw new IllegalStateException();
172: }
173: if (t.ordinal() > type.ordinal()) {
174: // restart keeping the track.
175: count = 1;
176: }
177: type = t;
178: }
179:
180: public void setAction(String a) {
181: action = a;
182: }
183:
184: protected boolean needsProcess() {
185: return id.equals(action);
186: }
187:
188: /**
189: * @return Whether action must be performed.
190: * @renderer Proposed renderer (State may decide to render another one, and return that)
191: * @throws IllegalStateException When renderers which should occur 'later' were already rendered,
192: * or when the belonging request was already 'ended'.
193:
194: */
195: public Renderer startBlock(Parameters frameworkParameters,
196: Renderer renderer) {
197: if (count == 0) {
198: throw new IllegalStateException("State " + this
199: + " was already marked for end.");
200: }
201: ++count;
202: setType(renderer != null ? renderer.getType()
203: : Renderer.Type.NOT);
204:
205: this .frameworkParameters = frameworkParameters;
206: log.debug("Start rendering for " + frameworkParameters);
207:
208: id = generateId(count);
209:
210: request.setAttribute(Framework.COMPONENT_ID_KEY, "mm_"
211: + getId());
212:
213: return renderer != null ? getRenderer(renderer) : null;
214: }
215:
216: /**
217: * Determins what should be rendered now.
218: * @param block a proposal
219: */
220: protected Renderer getRenderer(Renderer r) {
221: String blockName = request.getParameter("__b" + getId());
222: log.debug("found block " + blockName + " at parameters");
223: Block block = r.getBlock();
224: if (blockName == null) {
225: log.warn("No such block " + blockName);
226: return r;
227: } else {
228: Block toBlock = block.getComponent().getBlock(blockName);
229: log.debug("Using alternative block " + toBlock);
230: if (toBlock == null) {
231: throw new RuntimeException("No such block '"
232: + blockName + "' in " + block.getComponent());
233: }
234: return toBlock.getRenderer(r.getType());
235: }
236: }
237:
238: public void setBlock(Map<String, Object> map, Block toBlock) {
239: map.put("__b" + getId(), toBlock.getName());
240: }
241:
242: /**
243: * Puts this state in 'render' mode.
244: */
245: public void render(Renderer rend) {
246: renderer = rend;
247: localizeContext();
248: }
249:
250: /**
251: * Puts this state in 'process' mode
252: * @throws IllegalStateException If the renderer for block block was already rendered.
253: * or when the belonging request was already 'ended'.
254: */
255: public void process(Processor proc) {
256: if (renderer != null)
257: throw new IllegalStateException(); // works for basic-framework, which
258: // processes in render method.
259:
260: processor = proc;
261: localizeContext();
262:
263: }
264:
265: public Renderer getRenderer() {
266: return renderer;
267: }
268:
269: public Processor getProcessor() {
270: return processor;
271: }
272:
273: /**
274: * Returns the id of the current block, which uniquely identifies it on the current page (or
275: * http request).
276: */
277: public String getId() {
278: return id;
279: }
280:
281: protected String generateId(int c) {
282: return previousState == null ? "" + c : previousState.getId()
283: + '.' + c;
284: }
285:
286: /**
287: * If rendering not yet started, this returns the id of a component which would begin now.
288: */
289: public String getUpcomingId() {
290: return generateId(count + 1);
291: }
292:
293: public String toString() {
294: return "state:"
295: + getId()
296: + (isRendering() ? (":" + (renderer != null ? renderer
297: : processor)) : "");
298: }
299:
300: }
|