001/* 002 * Copyright 2011 Atteo. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014package org.atteo.evo.filtering; 015 016import java.util.ArrayList; 017 018import javax.annotation.Nullable; 019 020import org.w3c.dom.Element; 021import org.w3c.dom.NodeList; 022 023/** 024 * Get property value from XML by taking text content of a node pointed by property name. 025 * 026 * <p> 027 * The resolver tries to search for an element with a given property name. If none is found 028 * it tries to interpret dots (".") as separator between parent and children element names. 029 * For instance for document: 030 * <pre> 031 * {@code 032 * <a> 033 * <b> 034 * <c>test</c> 035 * </b> 036 * <e><f>test3</f></e> 037 * <e.f>test2</e.f> 038 * </a> 039 * } 040 * </pre> 041 * property {@code ${a.b.c}} will return 'test' value and property {@code ${a.e.f}} will return 'test2' value. 042 * </p> 043 */ 044public class XmlPropertyResolver extends SimplePropertyResolver { 045 private Element rootElement; 046 private boolean matchRoot; 047 048 /** 049 * Create new property resolver based on XML tree. 050 * @param rootElement root element of an XML tree to search for property value 051 * @param matchRoot whether root element should match, or matching should start from rootElement children 052 */ 053 public XmlPropertyResolver(@Nullable Element rootElement, boolean matchRoot) { 054 this.rootElement = rootElement; 055 this.matchRoot = matchRoot; 056 } 057 058 @Override 059 public String getProperty(String name) throws PropertyNotFoundException { 060 String value = getValue(name); 061 if (value == null) { 062 return null; 063 } 064 return value; 065 } 066 067 private String getValue(String name) { 068 if (rootElement == null) { 069 return null; 070 } 071 int position = 0; 072 ArrayList<Integer> dots = new ArrayList<Integer>(); 073 while (true) { 074 int index = name.indexOf(".", position); 075 if (index == -1) { 076 break; 077 } 078 dots.add(index); 079 position = index + 1; 080 } 081 dots.add(name.length()); 082 083 Element element = rootElement; 084 int dotIndex; 085 086 if (matchRoot) { 087 if (!rootElement.getNodeName().equals(name.substring(0, dots.get(0)))) { 088 return null; 089 } 090 091 position = dots.get(0) + 1; 092 dotIndex = 0; 093 } else { 094 position = 0; 095 dotIndex = -1; 096 } 097 098 outer: while (position < name.length()) { 099 String key = name.substring(position); 100 if (element.hasAttribute(key)) { 101 return element.getAttribute(key); 102 } 103 for (int i = dots.size() - 1; i > dotIndex; i--) { 104 key = name.substring(position, dots.get(i)); 105 NodeList list = element.getElementsByTagName(key); 106 if (list.getLength() == 1) { 107 element = (Element) list.item(0); 108 position = dots.get(i) + 1; 109 dotIndex = i; 110 continue outer; 111 } 112 } 113 return null; 114 } 115 // getTextContext() returns text content of all the children 116 // should we return only direct Text nodes elements content? 117 return element.getTextContent(); 118 } 119 120}