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;
018:
019: import org.apache.wicket.markup.ComponentTag;
020: import org.apache.wicket.markup.MarkupElement;
021: import org.apache.wicket.markup.MarkupStream;
022: import org.apache.wicket.markup.html.border.Border;
023: import org.apache.wicket.markup.html.panel.Fragment;
024:
025: /**
026: * Responding to an AJAX request requires that we position the markup stream at
027: * the component associated with the AJAX request. That is straight forward in
028: * most cases except for "transparent" components and for components which
029: * implement there own IComponentResolver.
030: *
031: * @author Juergen Donnerstag
032: */
033: final class MarkupFragmentFinder {
034: /**
035: * Construct
036: */
037: public MarkupFragmentFinder() {
038: }
039:
040: /**
041: * Get the markup stream and position it at the component
042: *
043: * @param component
044: * @return A MarkupStream which is positioned at the component
045: */
046: final MarkupStream find(final Component component) {
047: // Get the parent's associated markup stream.
048: MarkupContainer parentWithAssociatedMarkup = component
049: .findParentWithAssociatedMarkup();
050: MarkupStream markupStream = null;
051:
052: // Might be that we have to walk up the component hierarchy
053: while (true) {
054: markupStream = parentWithAssociatedMarkup
055: .getAssociatedMarkupStream(true);
056:
057: // In case the component has already been rendered, this is a
058: // performance short cut. But actually this was necessary because
059: // transparent containers and components which implement
060: // IComponentResolver destroy the 1:1 match between component path
061: // and markup path.
062: if (component.markupIndex != -1) {
063: // Might be that the markup has been reloaded and that the
064: // position has changed. Make sure the component is still
065: // available
066: try {
067: markupStream.setCurrentIndex(component.markupIndex);
068: MarkupElement elem = markupStream.get();
069: if (elem instanceof ComponentTag) {
070: ComponentTag tag = (ComponentTag) elem;
071: String id = tag.getId();
072: if ((id != null)
073: && id.equals(component.getId())) {
074: // Ok, found it
075: return markupStream;
076: }
077: }
078: } catch (IndexOutOfBoundsException ex) {
079: // fall through. Don't do anything
080: }
081: }
082:
083: // Make sure the markup stream is positioned at the correct element
084: String relativePath = getComponentRelativePath(component,
085: parentWithAssociatedMarkup);
086:
087: // If the component is defined in the markup
088: int index = markupStream.findComponentIndex(relativePath,
089: component.getId());
090: if (index != -1) {
091: // than position the stream at the beginning of the component
092: markupStream.setCurrentIndex(index);
093: return markupStream;
094: }
095:
096: if (parentWithAssociatedMarkup instanceof Fragment) {
097: markupStream = ((Fragment) parentWithAssociatedMarkup)
098: .findComponentIndex(component.getId());
099: return markupStream;
100: }
101:
102: // Yet another exception for Border in the code base.
103: // However if the container with the markup is a Border, than
104: // ...
105: if (parentWithAssociatedMarkup instanceof Border) {
106: parentWithAssociatedMarkup = parentWithAssociatedMarkup
107: .findParentWithAssociatedMarkup();
108: } else {
109: throw new WicketRuntimeException(
110: "Unable to find the markup for the component. That may be due to transparent containers or components implementing IComponentResolver: "
111: + component.toString());
112: }
113:
114: // Not found, reset the stream
115: markupStream = null;
116: }
117: }
118:
119: /**
120: * Get component path relativ to the parent container with associated markup
121: *
122: * @param component
123: * @param parentWithAssociatedMarkup
124: * @return the relativ path
125: */
126: private String getComponentRelativePath(final Component component,
127: final MarkupContainer parentWithAssociatedMarkup) {
128: final String componentPath = component.getParent()
129: .getPageRelativePath();
130: final String parentWithAssociatedMarkupPath = parentWithAssociatedMarkup
131: .getPageRelativePath();
132: String relativePath = componentPath
133: .substring(parentWithAssociatedMarkupPath.length());
134: if (relativePath.startsWith(":")) {
135: relativePath = relativePath.substring(1);
136: }
137: return relativePath;
138: }
139: }
|