001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.markup.html.border;
018:
019: import java.util.Locale;
020:
021: import org.apache.wicket.Application;
022: import org.apache.wicket.Component;
023: import org.apache.wicket.IComponentBorder;
024: import org.apache.wicket.MarkupContainer;
025: import org.apache.wicket.Response;
026: import org.apache.wicket.Session;
027: import org.apache.wicket.WicketRuntimeException;
028: import org.apache.wicket.markup.ContainerInfo;
029: import org.apache.wicket.markup.Markup;
030: import org.apache.wicket.markup.MarkupElement;
031: import org.apache.wicket.markup.MarkupResourceStream;
032: import org.apache.wicket.markup.MarkupStream;
033: import org.apache.wicket.markup.WicketTag;
034: import org.apache.wicket.markup.parser.filter.WicketTagIdentifier;
035: import org.apache.wicket.util.resource.IResourceStream;
036: import org.apache.wicket.util.resource.locator.IResourceStreamLocator;
037:
038: /**
039: * This is a IComponentBorder implementation that can be used if you have markup
040: * that should be around a component. It works just like {@link Border} so you
041: * have to have a <wicket:border>HTML before<wicket:body/>HTML after</wicket:border>
042: * in the html of your subclass.
043: *
044: * @author jcompagner
045: */
046: public class MarkupComponentBorder implements IComponentBorder {
047: static {
048: // register "wicket:border" and "wicket:body"
049: WicketTagIdentifier.registerWellKnownTagName(Border.BORDER);
050: WicketTagIdentifier.registerWellKnownTagName(Border.BODY);
051: }
052:
053: private static final long serialVersionUID = 1L;
054:
055: // markup stream associated with this border. bonus of keeping a reference
056: // is that when renderAfter starts the stream will be very close to its
057: // needed position because renderBefore has executed
058: private transient MarkupStream markupStream;
059:
060: /**
061: *
062: * @see wicket.IComponentBorder#renderBefore(wicket.Component)
063: */
064: public void renderBefore(Component component) {
065: final MarkupStream stream = getMarkupStream(component);
066: final Response response = component.getResponse();
067: stream.setCurrentIndex(0);
068:
069: boolean insideBorderMarkup = false;
070: while (stream.hasMore()) {
071: MarkupElement e = stream.next();
072: if (e instanceof WicketTag) {
073: WicketTag wt = (WicketTag) e;
074: if (!insideBorderMarkup) {
075: if (wt.isBorderTag() && wt.isOpen()) {
076: insideBorderMarkup = true;
077: continue;
078: } else {
079: throw new WicketRuntimeException(
080: "Unexpected tag encountered in markup of component border "
081: + getClass().getName()
082: + ". Tag: "
083: + wt.toString()
084: + ", expected tag: <wicket:border>");
085: }
086: } else {
087: if (wt.isBodyTag()) {
088: break;
089: } else {
090: throw new WicketRuntimeException(
091: "Unexpected tag encountered in markup of component border "
092: + getClass().getName()
093: + ". Tag: "
094: + wt.toString()
095: + ", expected tag: <wicket:body> or </wicket:body>");
096: }
097: }
098: }
099: if (insideBorderMarkup) {
100: response.write(e.toCharSequence());
101: }
102: }
103:
104: if (!stream.hasMore()) {
105: throw new WicketRuntimeException(
106: "Markup for component border "
107: + getClass().getName()
108: + " ended prematurely, was expecting </wicket:border>");
109: }
110: }
111:
112: /**
113: *
114: * @see wicket.IComponentBorder#renderAfter(wicket.Component)
115: */
116: public void renderAfter(Component component) {
117: final MarkupStream stream = getMarkupStream(component);
118: final Response response = component.getResponse();
119:
120: while (stream.hasMore()) {
121: MarkupElement e = stream.next();
122: if (e instanceof WicketTag) {
123: WicketTag wt = (WicketTag) e;
124: if (wt.isBorderTag() && wt.isClose()) {
125: break;
126: } else {
127: throw new WicketRuntimeException(
128: "Unexpected tag encountered in markup of component border "
129: + getClass().getName()
130: + ". Tag: "
131: + wt.toString()
132: + ", expected tag: </wicket:border>");
133: }
134: }
135: response.write(e.toCharSequence());
136: }
137: }
138:
139: private MarkupStream getMarkupStream(Component component) {
140: if (markupStream == null) {
141: markupStream = findMarkupStream(component);
142: }
143: return markupStream;
144: }
145:
146: private MarkupStream findMarkupStream(Component owner) {
147: final String markupType = getMarkupType(owner);
148:
149: // TODO we need to expose this functionality for any class not just for
150: // markupcontainers in markupcache so we dont have to replicate this
151: // logic here
152:
153: // Get locator to search for the resource
154: final IResourceStreamLocator locator = Application.get()
155: .getResourceSettings().getResourceStreamLocator();
156:
157: final Session session = Session.get();
158: final String style = session.getStyle();
159: final Locale locale = session.getLocale();
160:
161: MarkupResourceStream markupResourceStream = null;
162: Class containerClass = getClass();
163:
164: while (!(containerClass.equals(MarkupComponentBorder.class))) {
165: String path = containerClass.getName().replace('.', '/');
166: IResourceStream resourceStream = locator.locate(
167: containerClass, path, style, locale, markupType);
168:
169: // Did we find it already?
170: if (resourceStream != null) {
171: ContainerInfo ci = new ContainerInfo(containerClass,
172: locale, style, null, markupType);
173: markupResourceStream = new MarkupResourceStream(
174: resourceStream, ci, containerClass);
175: break;
176: }
177:
178: // Walk up the class hierarchy one level, if markup has not
179: // yet been found
180: containerClass = containerClass.getSuperclass();
181: }
182:
183: if (markupResourceStream == null) {
184: throw new WicketRuntimeException(
185: "Could not find markup for component border `"
186: + getClass().getName() + "`");
187: }
188:
189: try {
190: Markup markup = Application.get().getMarkupSettings()
191: .getMarkupParserFactory().newMarkupParser(
192: markupResourceStream).parse();
193: return new MarkupStream(markup);
194: } catch (Exception e) {
195: throw new WicketRuntimeException(
196: "Could not parse markup from markup resource stream: "
197: + markupResourceStream.toString());
198: }
199: }
200:
201: private String getMarkupType(Component component) {
202: String extension;
203: if (component instanceof MarkupContainer) {
204: extension = ((MarkupContainer) component).getMarkupType();
205: } else {
206: extension = component.getParent().getMarkupType();
207: }
208: return extension;
209: }
210: }
|