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.parser.filter;
018:
019: import java.text.ParseException;
020:
021: import org.apache.wicket.Component;
022: import org.apache.wicket.MarkupContainer;
023: import org.apache.wicket.RequestCycle;
024: import org.apache.wicket.behavior.AbstractBehavior;
025: import org.apache.wicket.behavior.IBehavior;
026: import org.apache.wicket.markup.ComponentTag;
027: import org.apache.wicket.markup.MarkupElement;
028: import org.apache.wicket.markup.MarkupStream;
029: import org.apache.wicket.markup.WicketTag;
030: import org.apache.wicket.markup.html.WebComponent;
031: import org.apache.wicket.markup.html.WebMarkupContainer;
032: import org.apache.wicket.markup.parser.AbstractMarkupFilter;
033: import org.apache.wicket.markup.resolver.IComponentResolver;
034: import org.slf4j.Logger;
035: import org.slf4j.LoggerFactory;
036:
037: /**
038: * The purpose of this filter is to make all "href", "src" and "background"
039: * attributes found in the markup which contain a relative URL like
040: * "myDir/myPage.gif" actually resolve in the output HTML, by prefixing them
041: * with with an appropriate path to make the link work properly, even if the
042: * current page is being displayed at a mounted URL or whatever. It is applied
043: * to all non wicket component tags, except for auto-linked tags.
044: *
045: * It achieves this by being both an IMarkupFilter and IComponentResolver, and
046: * works similarly to the <wicket:message> code. For each tag, we look to
047: * see if the path in "href", "src" and "background" attributes is relative. If
048: * it is, we assume it's relative to the context path and we should prefix it
049: * appropriately so that it resolves correctly for the current request, even if
050: * that's for something that's not at the context root. This is done for
051: * ServletWebRequests by prepending with "../" tokens, for example.
052: *
053: *
054: * @author Al Maw
055: */
056: public final class RelativePathPrefixHandler extends
057: AbstractMarkupFilter implements IComponentResolver {
058: private static final long serialVersionUID = 1L;
059:
060: /** Logging */
061: private static final Logger log = LoggerFactory
062: .getLogger(RelativePathPrefixHandler.class);
063:
064: /**
065: * The id automatically assigned to tags without an id which we need to
066: * prepend a relative path to.
067: */
068: public static final String WICKET_RELATIVE_PATH_PREFIX_CONTAINER_ID = "_relative_path_prefix_";
069:
070: /** List of attribute names considered */
071: private static final String attributeNames[] = new String[] {
072: "href", "src", "background" };
073:
074: /**
075: * Behavior that adds a prefix to src, href and background attributes to
076: * make them context-relative
077: */
078: public static final IBehavior RELATIVE_PATH_BEHAVIOR = new AbstractBehavior() {
079: private static final long serialVersionUID = 1L;
080:
081: public void onComponentTag(Component component, ComponentTag tag) {
082: String prefix = null;
083:
084: // Modify all relevant attributes
085: for (int i = 0; i < attributeNames.length; i++) {
086: String attrName = attributeNames[i];
087: String attrValue = tag.getAttributes().getString(
088: attrName);
089:
090: if ((attrValue != null)
091: && (attrValue.startsWith("/") == false)
092: && (attrValue.indexOf(":") < 0)
093: && !(attrValue.startsWith("#"))) {
094: if (prefix == null) {
095: prefix = RequestCycle.get().getRequest()
096: .getRelativePathPrefixToContextRoot();
097: }
098: attrValue = prefix + attrValue;
099: tag.getAttributes().put(attrName, attrValue);
100: }
101: }
102: }
103: };
104:
105: /**
106: * Get the next MarkupElement from the parent MarkupFilter and handle it if
107: * the specific filter criteria are met. Depending on the filter, it may
108: * return the MarkupElement unchanged, modified or it remove by asking the
109: * parent handler for the next tag.
110: *
111: * @see org.apache.wicket.markup.parser.IMarkupFilter#nextTag()
112: * @return Return the next eligible MarkupElement
113: */
114: public MarkupElement nextTag() throws ParseException {
115: // Get the next tag. If null, no more tags are available
116: final ComponentTag tag = (ComponentTag) getParent().nextTag();
117: if ((tag == null) || tag.isClose()) {
118: return tag;
119: }
120:
121: // Don't touch any wicket:id component and any auto-components
122: if ((tag instanceof WicketTag)
123: || (tag.isAutolinkEnabled() == true)
124: || (tag.getAttributes().get("wicket:id") != null)) {
125: return tag;
126: }
127:
128: // Work out whether we have any attributes that require us to add a
129: // behavior that prepends the relative path.
130: for (int i = 0; i < attributeNames.length; i++) {
131: String attrName = attributeNames[i];
132: String attrValue = tag.getAttributes().getString(attrName);
133: if ((attrValue != null)
134: && (attrValue.startsWith("/") == false)
135: && (attrValue.indexOf(":") < 0)
136: && !(attrValue.startsWith("#"))) {
137: if (tag.getId() == null) {
138: tag.setId(WICKET_RELATIVE_PATH_PREFIX_CONTAINER_ID);
139: tag.setAutoComponentTag(true);
140: }
141: tag.addBehavior(RELATIVE_PATH_BEHAVIOR);
142: tag.setModified(true);
143: break;
144: }
145: }
146:
147: return tag;
148: }
149:
150: /**
151: *
152: * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer,
153: * org.apache.wicket.markup.MarkupStream,
154: * org.apache.wicket.markup.ComponentTag)
155: */
156: public boolean resolve(MarkupContainer container,
157: MarkupStream markupStream, ComponentTag tag) {
158: if (WICKET_RELATIVE_PATH_PREFIX_CONTAINER_ID
159: .equals(tag.getId())) {
160: final Component wc;
161: String id = WICKET_RELATIVE_PATH_PREFIX_CONTAINER_ID
162: + container.getPage().getAutoIndex();
163: if (tag.isOpenClose()) {
164: wc = new WebComponent(id);
165: } else {
166: wc = new WebMarkupContainer(id);
167: }
168: container.autoAdd(wc, markupStream);
169: return true;
170: }
171: return false;
172: }
173: }
|