/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.sling.scripting.xproc.xpl.impl; import java.lang.reflect.Field; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.namespace.QName; import org.apache.sling.api.scripting.SlingScriptHelper; import org.apache.sling.scripting.xproc.xpl.api.XplElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractXplElementImpl implements XplElement { protected final Logger log = LoggerFactory.getLogger(this.getClass()); private SlingScriptHelper sling; private final Map<String, String> attributes = new HashMap<String, String>(); private XplElement parent; private final LinkedList<XplElement> children = new LinkedList<XplElement>(); private int depth = 0; public LinkedList<XplElement> getChildren() { return children; } public void addChild(XplElement child) { if (child == null) { String msg = "Element of class " + this.getClass().getName() + " received null child."; this.log.error(msg); throw new IllegalArgumentException(msg); } Field childField = this.getChildField(child); if (childField != null) { childField.setAccessible(true); try { childField.set(this, child); } catch (IllegalArgumentException e) { this.log.error("Failed to set child field for child class '" + child.getClass().getName(), e); } catch (IllegalAccessException e) { this.log.error("Failed to set child field for child class '" + child.getClass().getName(), e); } } child.setDepth(this.getDepth() + 1); this.children.add(child); child.setParent(this); } public void setAttributes(Map<String, String> attributes) { if (attributes == null || attributes.isEmpty()) { // nothing to do return; } // check for special attribute fields Map<String, Field> attributeFields = this.getAttributeFields(); for (Entry<String, String> entry : attributes.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); Field attributeField = attributeFields.get(key); if (attributeField != null) { attributeField.setAccessible(true); try { attributeField.set(this, value); } catch (IllegalArgumentException e) { String message = "Failed to set attribute field " + key; this.log.error(message, e); throw new RuntimeException(message, e); } catch (IllegalAccessException e) { String message = "Failed to set attribute field " + key; this.log.error(message, e); throw new RuntimeException(message, e); } } // default attribute processing this.processAttribute(key, value); } } public SlingScriptHelper getSling() { return sling; } public abstract QName getQName(); public XplElement getParent() { return parent; } public void setParent(XplElement parent) { this.parent = parent; } public int getDepth() { return depth; } public void setDepth(int depth) { this.depth = depth; } @Override public String toString() { StringBuffer sbXplElement = new StringBuffer(); addTabs(sbXplElement, getDepth()); sbXplElement.append("<"); sbXplElement.append("p:" + this.getQName().getLocalPart()); if (getDepth() == 0) { sbXplElement.append(" xmlns:p=\"http://www.w3.org/ns/xproc\""); } for (String attribute : this.attributes.keySet()) { sbXplElement.append(" "); sbXplElement.append(attribute + "=" + "\"" + this.attributes.get(attribute) + "\""); } if (getChildren().size() == 0) { sbXplElement.append(" />"); return sbXplElement.toString(); } sbXplElement.append(">"); for (XplElement child : this.getChildren()) { sbXplElement.append("\r\n"); sbXplElement.append(child.toString()); } sbXplElement.append("\r\n"); addTabs(sbXplElement, getDepth()); sbXplElement.append("</"); sbXplElement.append("p:" + this.getQName().getLocalPart()); sbXplElement.append(">"); return sbXplElement.toString(); } private void addTabs(StringBuffer sb, int num) { for (int i = 0; i < num; i++) sb.append("\t"); } protected void processAttribute(String key, String value) { this.attributes.put(key, value); } private Field getChildField(XplElement child) { Class<?> currentClass = this.getClass(); Field[] declaredFields = currentClass.getDeclaredFields(); for (Field declaredField : declaredFields) { if (declaredField.getType().isAssignableFrom(child.getClass())) { return declaredField; } } return null; } private Map<String, Field> getAttributeFields() { Map<String, Field> attributeFields = new HashMap<String, Field>(); Class<?> currentClass = this.getClass(); while (currentClass != null) { Field[] declaredFields = currentClass.getDeclaredFields(); for (Field declaredField : declaredFields) { String fieldName = this.convertCamelCase(declaredField.getName()); attributeFields.put(fieldName, declaredField); } currentClass = currentClass.getSuperclass(); } return attributeFields; } private String convertCamelCase(String name) { Pattern camelCasePattern = Pattern.compile("(.)([A-Z])"); Matcher matcher = camelCasePattern.matcher(name); int lastMatch = 0; StringBuilder result = new StringBuilder(); while (matcher.find()) { result.append(name.substring(lastMatch, matcher.start())); result.append(matcher.group(1)); result.append("-"); result.append(matcher.group(2).toLowerCase()); lastMatch = matcher.end(); } result.append(name.substring(lastMatch, name.length())); return result.toString(); } }