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.Application;
022: import org.apache.wicket.WicketRuntimeException;
023: import org.apache.wicket.markup.ComponentTag;
024: import org.apache.wicket.markup.MarkupElement;
025: import org.apache.wicket.markup.WicketTag;
026: import org.apache.wicket.markup.parser.AbstractMarkupFilter;
027: import org.apache.wicket.util.collections.ArrayListStack;
028: import org.apache.wicket.util.string.StringValueConversionException;
029: import org.apache.wicket.util.string.Strings;
030: import org.apache.wicket.util.value.IValueMap;
031:
032: /**
033: * This is a markup inline filter. It identifies xml tags which include a href
034: * attribute and which are not Wicket specific components and flags these tags
035: * (ComponentTag) as autolink enabled. A component resolver will later resolve
036: * the href and assign a BookmarkablePageLink to it (automatically).
037: * <p>
038: * An application setting is used as default value, which might be modified for
039: * specific regions. These regions are identified by <wicket:link> tags
040: * with an optional 'autolink' attribute. The default value for the attribute is
041: * true, thus enabling autolinking. An open-close <wicket:link/> tag will
042: * change the autolink status until the end of the markup document or the next
043: * <wicket:link> tag respectively. <wicket:link> regions may be
044: * nested.
045: *
046: * @author Juergen Donnerstag
047: */
048: public class WicketLinkTagHandler extends AbstractMarkupFilter {
049: /** The id of autolink components */
050: public static final String AUTOLINK_ID = "_autolink_";
051:
052: static {
053: // register "wicket:link"
054: WicketTagIdentifier.registerWellKnownTagName("link");
055: }
056:
057: /** Allow to have link regions within link regions */
058: private ArrayListStack autolinkStatus;
059:
060: /** Current status */
061: private boolean autolinking = true;
062:
063: /**
064: * Construct.
065: */
066: public WicketLinkTagHandler() {
067: setAutomaticLinking(Application.get().getMarkupSettings()
068: .getAutomaticLinking());
069: }
070:
071: /**
072: * Set the default value for autolinking
073: *
074: * @param enable
075: * if true, autolinks are enabled
076: */
077: public void setAutomaticLinking(final boolean enable) {
078: autolinking = enable;
079: }
080:
081: /**
082: * Get the next MarkupElement from the parent MarkupFilter and handles it if
083: * the specific filter criteria are met. Depending on the filter, it may
084: * return the MarkupElement unchanged, modified or it remove by asking the
085: * parent handler for the next tag.
086: *
087: * @see org.apache.wicket.markup.parser.IMarkupFilter#nextTag()
088: * @return Return the next eligible MarkupElement
089: */
090: public final MarkupElement nextTag() throws ParseException {
091: // Get next tag. Null, if no more tag available
092: final ComponentTag tag = (ComponentTag) getParent().nextTag();
093: if (tag == null) {
094: return tag;
095: }
096:
097: // Only xml tags not already identified as Wicket components will be
098: // considered for autolinking. This is because it is assumed that Wicket
099: // components like images or all other kind of Wicket Links will handle
100: // it themselves.
101: // Subclass analyzeAutolinkCondition() to implement you own
102: // implementation and register the new tag handler with the markup
103: // parser through Application.newMarkupParser().
104: if ((autolinking == true)
105: && (analyzeAutolinkCondition(tag) == true)) {
106: // Mark it as autolink enabled
107: tag.enableAutolink(true);
108:
109: // Just a dummy name. The ComponentTag will not be forwarded.
110: tag.setId(AUTOLINK_ID);
111: tag.setAutoComponentTag(true);
112: tag.setModified(true);
113: return tag;
114: }
115:
116: // For all <wicket:link ..> tags which probably change the
117: // current autolink status.
118: if (tag instanceof WicketTag) {
119: final WicketTag wtag = (WicketTag) tag;
120: if (wtag.isLinkTag()) {
121: // Beginning of the region
122: if (tag.isOpen() || tag.isOpenClose()) {
123: if (tag.isOpen()) {
124: if (autolinkStatus == null) {
125: autolinkStatus = new ArrayListStack();
126: }
127:
128: // remember the current setting to be reset after the
129: // region
130: autolinkStatus.push(new Boolean(autolinking));
131: }
132:
133: // html allows to represent true in different ways
134: final String autolink = tag.getAttributes()
135: .getString("autolink");
136: try {
137: autolinking = Strings.isEmpty(autolink)
138: || Strings.isTrue(autolink);
139: } catch (StringValueConversionException e) {
140: throw new WicketRuntimeException(
141: "Invalid autolink attribute value \""
142: + autolink + "\"");
143: }
144: } else if (tag.isClose()) {
145: // restore the autolink setting from before the region
146: autolinking = ((Boolean) autolinkStatus.pop())
147: .booleanValue();
148: }
149:
150: return wtag;
151: }
152: }
153:
154: return tag;
155: }
156:
157: /**
158: * Analyze the tag. If return value == true, a autolink component will be
159: * created.
160: * <p>
161: * Subclass analyzeAutolinkCondition() to implement you own implementation
162: * and register the new tag handler with the markup parser through
163: * Application.newMarkupParser().
164: *
165: * @param tag
166: * The current tag being parsed
167: * @return If true, tag will become auto-component
168: */
169: protected boolean analyzeAutolinkCondition(final ComponentTag tag) {
170: if (tag.getId() == null) {
171: IValueMap attributes = tag.getAttributes();
172: String ref = attributes.getString("href");
173: if (checkRef(ref)) {
174: return true;
175: }
176: ref = attributes.getString("src");
177: if (checkRef(ref)) {
178: return true;
179: }
180: }
181:
182: return false;
183: }
184:
185: /**
186: *
187: * @param ref
188: * @return
189: */
190: private final boolean checkRef(String ref) {
191: return (ref != null) && (ref.indexOf(":") == -1);
192: }
193: }
|