/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.brixcms.markup.transform;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.markup.parser.filter.EnclosureHandler;
import org.brixcms.markup.MarkupHelper;
import org.brixcms.markup.MarkupSource;
import org.brixcms.markup.tag.ComponentTag;
import org.brixcms.markup.tag.Item;
import org.brixcms.markup.tag.Tag;
import org.brixcms.plugin.site.page.AbstractContainer;
import org.brixcms.plugin.site.page.tile.TileTag;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Enables the use of wicket:enclosures with brix:tile tags.
* <p/>
* It will resolve the enclosure child elements based on either "brix:tile id" or wicket:id attributes. You can
* reference wicket components within the tile using the 'child' attribute and a comma separated path.
* <p/>
* e.g. where the tile has the wicket component title as a child of wrapper <wicket:enclosure
* child="mytile,wrapper,title"> <brix:tile id="mytile" /> </wicket:enclosure>
* <p/>
* TODO: - investigate if it is possible to have commas in brix:tile id or wicket:id as this would break id matching -
* investigate if this works with wicket auto-tags
*
* @author James McIntosh
*/
public class EnclosureMarkupSourceTransformer extends MarkupSourceTransformer {
public EnclosureMarkupSourceTransformer(MarkupSource delegate) {
super(delegate);
}
private String getEnclosureTag() {
return Application.get().getMapperContext().getNamespace()
+ ":enclosure";
}
private String getIdAttribute() {
return Application.get().getMapperContext().getNamespace() + ":id";
}
@Override
protected List<Item> transform(List<Item> items) {
Tag enclosure = null;
List<String> children = null;
List<Tag> enclosureChildTags = null;
for (Item i : items) {
if (i instanceof Tag) {
Tag tag = (Tag) i;
if (isEnclosure(tag)) {
if (tag.getType() == Tag.Type.OPEN) {
// found opening enclosure
enclosure = tag;
children = getChildren(enclosure);
enclosureChildTags = new ArrayList<Tag>();
} else if (tag.getType() == Tag.Type.CLOSE) {
// tidy up on close tag
updateEnclosureChildId(enclosure, children,
enclosureChildTags);
enclosure = null;
children = null;
enclosureChildTags = null;
}
} else if (enclosure != null && !children.isEmpty()) {
// if we have open enclosure tag and children to find
int index = checkIfTagIsChild(tag, children,
enclosureChildTags);
if (index != -1) {
onChildFound(tag, children, enclosureChildTags, index);
}
}
}
}
return items;
}
private boolean isEnclosure(Tag tag) {
return getEnclosureTag().equals(tag.getName());
}
/**
* Gets the value of the "child" attribute and splits the path into a list
*
* @param enclosure
* @return ids
*/
private List<String> getChildren(Tag enclosure) {
Map<String, String> attributes = enclosure.getAttributeMap();
if (attributes != null) {
if (attributes.containsKey(EnclosureHandler.CHILD_ATTRIBUTE)) {
// split for nested components
String[] children = attributes.get(
EnclosureHandler.CHILD_ATTRIBUTE).split(
"" + Component.PATH_SEPARATOR);
ArrayList<String> list = new ArrayList<String>();
for (String child : children) {
list.add(child);
}
return list;
}
}
return null;
}
/**
* Replaces the supplied enclosure child tag with the actual resolved comma separated component path
*
* @param enclosure
* @param children
* @param enclosureChildTags
*/
private void updateEnclosureChildId(Tag enclosure, List<String> children,
List<Tag> enclosureChildTags) {
Map<String, String> attributes = enclosure.getAttributeMap();
if (attributes != null) {
String child = "";
for (int i = 0; i < children.size(); i++) {
String childid = children.get(i);
if (childid != null) {
child += (child.length() > 0 ? Component.PATH_SEPARATOR
: "") + childid;
} else {
Tag tag = enclosureChildTags.get(i);
String id = getGeneratedTagId(tag);
if (id != null) {
// nested children are delimited by commas
child += (child.length() > 0 ? Component.PATH_SEPARATOR
: "") + id;
}
}
}
attributes.put(EnclosureHandler.CHILD_ATTRIBUTE, child);
}
}
/**
* Resolved the actual generated component wicket:id for the tag
*
* @param tag
* @return
*/
private String getGeneratedTagId(Tag tag) {
if (tag instanceof ComponentTag) {
return MarkupHelper.getComponentID((ComponentTag) tag);
}
return null;
}
/**
* Inspects the current tag and determine if it is one of the required child components for the enclosure
*
* @param tag
* @param children
* @param enclosureChildTags
* @return
*/
private int checkIfTagIsChild(Tag tag, List<String> children,
List<Tag> enclosureChildTags) {
String id = getTagId(tag);
if (id != null) {
for (String child : children) {
if (child == null) {
continue;
}
if (child.equals(id)) {
return children.indexOf(child);
}
}
}
return -1;
}
/**
* Checks for an id attribute if a brix:tile then a wicket:id
*
* @param tag
* @return
*/
private String getTagId(Tag tag) {
Map<String, String> attributes = tag.getAttributeMap();
String id = null;
if (attributes != null) {
// look for brix:tile first "id" first then "wicket:id"
if (tag instanceof TileTag) {
id = attributes.get(AbstractContainer.MARKUP_TILE_ID);
}
if (id == null) {
id = attributes.get(getWicketIdAttributeName(tag));
}
return id;
}
return null;
}
private String getWicketIdAttributeName(Tag tag) {
// TODO: this reference possibly could be smarter by somehow using
// ComponentTag.getId()
// ComponentTag comments indicate that wicket:id is the default and that
// this could
// potentially be different in cases such as auto-tags
return getIdAttribute();
}
/**
* Updates the list of remaining child components to find and the list of already found tags which represent them
*
* @param tag
* @param children
* @param enclosureChildTags
* @param index
*/
private void onChildFound(Tag tag, List<String> children,
List<Tag> enclosureChildTags, int index) {
if (index != -1) {
children.remove(index);
children.add(index, null);
enclosureChildTags.add(index, tag);
}
}
}