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