001: /*
002: * $Id: Border.java 4831 2006-03-08 13:32:22 -0800 (Wed, 08 Mar 2006)
003: * jdonnerstag $ $Revision: 460256 $ $Date: 2006-03-08 13:32:22 -0800 (Wed, 08 Mar
004: * 2006) $
005: *
006: * ==============================================================================
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: */
019: package wicket.markup.html.border;
020:
021: import wicket.MarkupContainer;
022: import wicket.Response;
023: import wicket.markup.ComponentTag;
024: import wicket.markup.MarkupStream;
025: import wicket.markup.WicketTag;
026: import wicket.markup.html.WebMarkupContainerWithAssociatedMarkup;
027: import wicket.markup.html.internal.HtmlHeaderContainer;
028: import wicket.markup.parser.XmlTag;
029: import wicket.markup.parser.filter.WicketTagIdentifier;
030: import wicket.markup.resolver.IComponentResolver;
031: import wicket.model.IModel;
032: import wicket.response.NullResponse;
033:
034: /**
035: * A border component has associated markup which is drawn and determines
036: * placement of any markup and/or components nested within the border component.
037: * <p>
038: * The portion of the border's associated markup file which is to be used in
039: * rendering the border is denoted by a <wicket:border> tag. The children
040: * of the border component instance are then inserted into this markup,
041: * replacing the first <wicket:body> tag in the border's associated
042: * markup.
043: * <p>
044: * For example, if a border's associated markup looked like this:
045: *
046: * <pre>
047: * <html>
048: * <body>
049: * <wicket:border>
050: * First <wicket:body/> Last
051: * </wicket:border>
052: * </body>
053: * </html>
054: * </pre>
055: *
056: * And the border was used on a page like this:
057: *
058: * <pre>
059: * <html>
060: * <body>
061: * <span wicket:id = "myBorder">
062: * Middle
063: * </span>
064: * </body>
065: * </html>
066: * </pre>
067: *
068: * Then the resulting HTML would look like this:
069: *
070: * <pre>
071: * <html>
072: * <body>
073: * First Middle Last
074: * </body>
075: * </html>
076: * </pre>
077: *
078: * In other words, the body of the myBorder component is substituted into the
079: * border's associated markup at the position indicated by the
080: * <wicket:body> tag.
081: * <p>
082: * Regarding <wicket:body/> you have two options. Either use
083: * <wicket:body/> (open-close tag) which will automatically be expanded to
084: * <wicket:body>body content</wicket:body> or use
085: * <wicket:body>preview region</wicket:body> in your border's
086: * markup. The preview region (everything in between the open and close tag)
087: * will automatically be removed.
088: *
089: * @author Jonathan Locke
090: */
091: public abstract class Border extends
092: WebMarkupContainerWithAssociatedMarkup implements
093: IComponentResolver {
094: static {
095: // register "wicket:fragement"
096: WicketTagIdentifier.registerWellKnownTagName("border");
097: WicketTagIdentifier.registerWellKnownTagName("body");
098: }
099:
100: /** Will be true, once the first <wicket:body> has been seen */
101: private transient boolean haveSeenBodyTag = false;
102:
103: /** The open tag for this border component. */
104: private transient ComponentTag openTag;
105:
106: /** Should be true for bordered pages */
107: private boolean transparentResolver = false;
108:
109: /** If false, the content of <wicket:body> will not be printed */
110: private boolean bodyVisible = true;
111:
112: /**
113: * @see wicket.Component#Component(String)
114: */
115: public Border(final String id) {
116: super (id);
117: }
118:
119: /**
120: * @see wicket.Component#Component(String, IModel)
121: */
122: public Border(final String id, final IModel model) {
123: super (id, model);
124: }
125:
126: /**
127: * When this method is called with a false value the components and raw
128: * markup that this border wraps will not be rendered.
129: *
130: * @param bodyVisible
131: * @return this for chaining
132: */
133: public Border setBorderBodyVisible(boolean bodyVisible) {
134: this .bodyVisible = bodyVisible;
135: return this ;
136: }
137:
138: /**
139: *
140: * @see wicket.MarkupContainer#isTransparentResolver()
141: */
142: public boolean isTransparentResolver() {
143: return transparentResolver;
144: }
145:
146: /**
147: * Borders used for bordered pages should set it to "true". Default is
148: * "false".
149: *
150: * @param transparentResolver
151: * @return this for chaining
152: */
153: public final Border setTransparentResolver(
154: final boolean transparentResolver) {
155: this .transparentResolver = transparentResolver;
156: return this ;
157: }
158:
159: /**
160: * Border makes use of a <wicket:body> tag to identify the position to
161: * insert within the border's body. As <wicket:body> is a special tag
162: * and MarkupContainer is not able to handle it, we do that here.
163: * <p>
164: * You have two options. Either use <wicket:body/> (open-close tag)
165: * which will automatically be expanded to <wicket:body>body
166: * content</wicket:body> or use <wicket:body>preview
167: * region</wicket:body> in your border's markup. The preview region
168: * (everything in between the open and close tag) will automatically be
169: * removed.
170: *
171: * @see IComponentResolver#resolve(MarkupContainer, MarkupStream,
172: * ComponentTag)
173: *
174: * @param container
175: * The container parsing its markup
176: * @param markupStream
177: * The current markupStream
178: * @param tag
179: * The current component tag while parsing the markup
180: * @return True if componentId was handled by the resolver, false otherwise.
181: */
182: public final boolean resolve(final MarkupContainer container,
183: final MarkupStream markupStream, final ComponentTag tag) {
184: // Determine if tag is a <wicket:body> tag
185: if (!(tag instanceof WicketTag)) {
186: return false;
187: }
188:
189: final WicketTag wtag = (WicketTag) tag;
190: if (!wtag.isBodyTag()) {
191: return false;
192: }
193:
194: final Response originalResponse;
195: if (this .bodyVisible == true) {
196: originalResponse = null;
197: } else {
198: originalResponse = getRequestCycle().setResponse(
199: NullResponse.getInstance());
200: }
201:
202: try {
203: renderBodyComponent(markupStream, wtag);
204: } finally {
205: if (originalResponse != null) {
206: getRequestCycle().setResponse(originalResponse);
207: }
208: }
209:
210: return true;
211: }
212:
213: /**
214: * Render the tag body
215: *
216: * @see wicket.Component#onComponentTagBody(wicket.markup.MarkupStream,
217: * wicket.markup.ComponentTag)
218: */
219: protected final void onComponentTagBody(
220: final MarkupStream markupStream, final ComponentTag openTag) {
221: // Save open tag for callback later to render body
222: this .openTag = openTag;
223:
224: // initialize
225: this .haveSeenBodyTag = false;
226:
227: // Render the associated markup
228: renderAssociatedMarkup("border",
229: "Markup for a border component must begin a tag like '<wicket:border>'");
230:
231: // There shall exactly only one body tag per border
232: if (haveSeenBodyTag == false) {
233: markupStream
234: .throwMarkupException("Didn't find <wicket:body/> tag for the border compoment.");
235: }
236: }
237:
238: /**
239: *
240: * @see wicket.Component#renderHead(wicket.markup.html.internal.HtmlHeaderContainer)
241: */
242: public void renderHead(HtmlHeaderContainer container) {
243: this .renderHeadFromAssociatedMarkupFile(container);
244: super .renderHead(container);
245: }
246:
247: /**
248: * Render the wicket:body and all what is in it.
249: *
250: * @param markupStream
251: * The associated markup stream
252: * @param wtag
253: * The wicket:body tag
254: */
255: public void renderBodyComponent(final MarkupStream markupStream,
256: final WicketTag wtag) {
257: // Ok, it is a wicket:body tag. Now render its body
258: final ComponentTag bodyTag = renderBodyComponentTag(
259: markupStream, wtag);
260:
261: // If markup stream is null, that indicates we already recursed into
262: // this block of log and set it to null (below). If we did that,
263: // then we want to go up another level of border nesting.
264: Border border = this ;
265: if (border.getMarkupStream() == null) {
266: // Find Border at or above parent of this border
267: final MarkupContainer borderParent = border.getParent();
268: border = (Border) ((borderParent instanceof Border) ? borderParent
269: : borderParent.findParent(Border.class));
270: }
271:
272: // Get the border's markup
273: final MarkupStream borderMarkup = border.findMarkupStream();
274:
275: // Set markup of border to null. This allows us to find the border's
276: // parent's markup. It also indicates that we've been here in the
277: // log just above.
278: border.setMarkupStream(null);
279:
280: // Draw the children of the border component using its original
281: // in-line markup stream (not the border's associated markup stream)
282: border.renderComponentTagBody(border.findMarkupStream(),
283: border.openTag);
284:
285: // Restore border markup so it can continue rendering
286: border.setMarkupStream(borderMarkup);
287:
288: // Render body close tag: </wicket:body>
289: if (wtag.isOpenClose()) {
290: markupStream.next();
291: bodyTag.setType(XmlTag.CLOSE);
292: renderComponentTag(bodyTag);
293: }
294:
295: // There shall exactly only one body tag per border
296: if (border.haveSeenBodyTag == true) {
297: markupStream
298: .throwMarkupException("There must be exactly one <wicket:body> tag for each border compoment.");
299: }
300:
301: border.haveSeenBodyTag = true;
302: }
303:
304: /**
305: * Render the wicket:body tag
306: *
307: * @param markupStream
308: * The associated markup stream
309: * @param tag
310: * The wicket:body tag
311: */
312: public void renderBodyComponentTagBody(
313: final MarkupStream markupStream, final ComponentTag tag) {
314: renderComponentTagBody(markupStream, tag);
315: }
316:
317: /**
318: * Render the wicket:body tag
319: *
320: * @param tag
321: * The wicket:body tag
322: * @param markupStream
323: * The associated markup stream
324: * @return the body tag. May be its type has been changed
325: */
326: protected ComponentTag renderBodyComponentTag(
327: final MarkupStream markupStream, final ComponentTag tag) {
328: ComponentTag bodyTag = tag;
329:
330: // Ok, it is a wicket:body tag. Now render its body
331: if (tag.isOpen()) {
332: // It is open-preview-close already.
333: // Only RawMarkup is allowed within the preview region, which
334: // gets stripped from output
335: markupStream.next();
336: markupStream.skipRawMarkup();
337: } else if (tag.isOpenClose()) {
338: // Automatically expand <wicket:body/> to
339: // <wicket:body>...</wicket:body>
340: // in order for the html to look right: insert the body in between
341: // the wicket tags instead of behind the open-close tag.
342: bodyTag = tag.mutable();
343: bodyTag.setType(XmlTag.OPEN);
344: } else {
345: markupStream
346: .throwMarkupException("A <wicket:body> tag must be an open or open-close tag.");
347: }
348:
349: renderComponentTag(bodyTag);
350: return bodyTag;
351: }
352: }
|