001/* 002 * **** BEGIN LICENSE BLOCK ***** 003 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 004 * 005 * The contents of this file are subject to the Mozilla Public License Version 006 * 1.1 (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 * http://www.mozilla.org/MPL/ 009 * 010 * Software distributed under the License is distributed on an "AS IS" basis, 011 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 012 * for the specific language governing rights and limitations under the 013 * License. 014 * 015 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 016 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 017 * 018 * The Initial Developer of the Original Code is 019 * Agfa Healthcare. 020 * Portions created by the Initial Developer are Copyright (C) 2014 021 * the Initial Developer. All Rights Reserved. 022 * 023 * Contributor(s): 024 * See @authors listed below 025 * 026 * Alternatively, the contents of this file may be used under the terms of 027 * either the GNU General Public License Version 2 or later (the "GPL"), or 028 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 029 * in which case the provisions of the GPL or the LGPL are applicable instead 030 * of those above. If you wish to allow use of your version of this file only 031 * under the terms of either the GPL or the LGPL, and not to allow others to 032 * use your version of this file under the terms of the MPL, indicate your 033 * decision by deleting the provisions above and replace them with the notice 034 * and other provisions required by the GPL or the LGPL. If you do not delete 035 * the provisions above, a recipient may use your version of this file under 036 * the terms of any one of the MPL, the GPL or the LGPL. 037 * 038 * ***** END LICENSE BLOCK ***** 039 */ 040package org.dcm4che3.conf.core; 041 042import org.apache.commons.jxpath.AbstractFactory; 043import org.apache.commons.jxpath.JXPathContext; 044import org.apache.commons.jxpath.JXPathNotFoundException; 045import org.apache.commons.jxpath.Pointer; 046import org.dcm4che3.conf.core.api.Configuration; 047import org.dcm4che3.conf.core.util.XNodeUtil; 048 049import java.util.*; 050import java.util.Map.Entry; 051import java.util.regex.Matcher; 052import java.util.regex.Pattern; 053 054public class Nodes { 055 056 057 private static Pattern itemPattern = Pattern.compile("/(?<item>(\\\\/|[^/\\[\\]@\\*])*)"); 058 private static Pattern simplePathPattern = Pattern.compile("(" + itemPattern + ")*"); 059 060 public static String concat(String path1, String path2) { 061 String res = path1 + "/" + path2; 062 return res.replace("///", "/").replace("//", "/"); 063 } 064 065 public static Object getNode(Object rootConfigNode, String path) { 066 try { 067 return JXPathContext.newContext(rootConfigNode).getValue(path); 068 } catch (JXPathNotFoundException e) { 069 return null; 070 } 071 } 072 073 public static boolean nodeExists(Map<String, Object> rootConfigNode, String path) { 074 return getNode(rootConfigNode, path) != null; 075 } 076 077 public static void removeNodes(Map<String, Object> configurationRoot, String path) { 078 JXPathContext.newContext(configurationRoot).removeAll(path); 079 } 080 081 public static Iterator search(Map<String, Object> configurationRoot, String liteXPathExpression) throws IllegalArgumentException { 082 return JXPathContext.newContext(configurationRoot).iterate(liteXPathExpression); 083 084 } 085 086 /** 087 * Clones structure but re-uses primitives 088 * 089 * @param node 090 * @return 091 */ 092 @SuppressWarnings("unchecked") 093 public static Object deepCloneNode(Object node) { 094 095 096 if (isPrimitive(node)) return node; 097 098 if (node instanceof Collection) { 099 Collection givenCollection = (Collection) node; 100 ArrayList<Object> newCollection = new ArrayList<Object>(givenCollection.size()); 101 for (Object o : givenCollection) newCollection.add(deepCloneNode(o)); 102 return newCollection; 103 } 104 105 if (node instanceof Map) { 106 Map givenMapNode = (Map) node; 107 Map newMapNode = new HashMap(givenMapNode.size()); 108 for (Entry e : (Set<Entry>) givenMapNode.entrySet()) { 109 newMapNode.put(e.getKey(), deepCloneNode(e.getValue())); 110 } 111 112 return newMapNode; 113 } 114 115 throw new IllegalArgumentException("Unexpected node type " + node.getClass()); 116 } 117 118 public static boolean isPrimitive(Object value) { 119 return value == null || 120 value instanceof Number || 121 value instanceof String || 122 value instanceof Boolean; 123 } 124 125 126 /** 127 * traverses nodenames and also @name predicates, so something like this 128 * /dicomConfigurationRoot/dicomDevicesRoot[@name='deviceName']/deviceExtensions 129 * will return 130 * dicomConfigurationRoot,dicomDevicesRoot,deviceName,deviceExtensions 131 * 132 * @param path 133 * @return 134 */ 135 public static List<Object> getPathItems(String path) { 136 List<Map<String, Object>> refItems = XNodeUtil.parseReference(path); 137 List<Object> names = new ArrayList<Object>(); 138 139 for (Map<String, Object> refItem : refItems) { 140 names.add((String) refItem.get("$name")); 141 if (refItem.containsKey("@name")) 142 names.add((String) refItem.get("@name")); 143 } 144 145 return names; 146 } 147 148 /** 149 * @param path path str to parse 150 * @return list of path items in case the provided path 151 * <ul> 152 * <li>is simple (e.g. "/dicomConfigurationRoot/globalConfiguration/dcmTransferCapabilities" )</li> 153 * <li>is persistable (e.g. "/dicomConfigurationRoot/dicomDevicesRoot[@name='someName']")</li> 154 * </ul> 155 * <p/> 156 * otherwise <b>null</b> 157 */ 158 public static List<Object> simpleOrPersistablePathToPathItemsOrNull(String path) { 159 160 List<Map<String, Object>> refItems; 161 try { 162 refItems = XNodeUtil.parseReference(path); 163 } catch (IllegalArgumentException e) { 164 return null; 165 } 166 167 List<Object> pathItems = new ArrayList<Object>(); 168 169 for (Map<String, Object> refItem : refItems) { 170 171 String name = null; 172 String attrName = null; 173 174 for (Entry<String, Object> stringObjectEntry : refItem.entrySet()) { 175 176 if (stringObjectEntry.getKey().equals("$name")) 177 name = ((String) stringObjectEntry.getValue()); 178 else if (stringObjectEntry.getKey().equals("@name")) 179 attrName = ((String) stringObjectEntry.getValue()); 180 else { 181 // this path is neither simple nor persistable 182 return null; 183 } 184 185 if (((String) stringObjectEntry.getValue()).contains("*")) 186 return null; 187 } 188 189 // this path is neither simple nor persistable 190 if (name == null) 191 return null; 192 193 pathItems.add(name); 194 195 if (attrName != null) 196 pathItems.add(attrName); 197 198 } 199 200 return pathItems; 201 } 202 203 public static String toSimpleEscapedPath(Iterator<Object> items) { 204 ArrayList<Object> strings = new ArrayList<Object>(); 205 while (items.hasNext()) 206 strings.add(items.next()); 207 return toSimpleEscapedPath(strings); 208 } 209 210 public static String toSimpleEscapedPath(Iterable<Object> items) { 211 String s = ""; 212 for (Object item : items) s += "/" + item.toString().replace("/", "\\/"); 213 return s; 214 } 215 216 public static List<String> fromSimpleEscapedPath(String path) { 217 List<String> strings = fromSimpleEscapedPathOrNull(path); 218 if (strings == null) 219 throw new IllegalArgumentException("Simple path " + path + " is invalid"); 220 return strings; 221 } 222 223 public static List<String> fromSimpleEscapedPathOrNull(String path) { 224 225 if (!simplePathPattern.matcher(path).matches()) 226 return null; 227 228 Matcher matcher = itemPattern.matcher(path); 229 230 List<String> list = new ArrayList<String>(); 231 while (matcher.find()) { 232 list.add(matcher.group("item").replace("\\/", "/")); 233 } 234 235 return list; 236 } 237 238 public static void replacePrimitive(Map<String, Object> map, Object replacement, List<Object> pathItems) { 239 240 if (pathItems.isEmpty()) 241 throw new IllegalArgumentException("Cannot replace root with a primitive"); 242 243 replaceObject(map, replacement, pathItems); 244 245 } 246 247 public static Map<String, Object> replaceNode(Map<String, Object> map, Map<String, Object> replacement, List<Object> pathItems) { 248 return (Map<String, Object>) replaceObject(map, replacement, pathItems); 249 } 250 251 private static Object replaceObject(Map<String, Object> map, Object replacement, List<Object> pathItems) { 252 Object node = map; 253 Object subNode = node; 254 Object name = null; 255 256 if (pathItems.isEmpty()) 257 return replacement; 258 259 for (Object pathItem : pathItems) { 260 name = pathItem; 261 262 node = subNode; 263 subNode = getElement(node, pathItem); 264 265 if (subNode == null) { 266 267 if (!(name instanceof String)) 268 throw new IllegalStateException("Cannot create lists items with replace, path " + pathItems + " , item '" + name + "' , node " + node); 269 270 subNode = Configuration.NodeFactory.emptyNode(); 271 ((Map)node).put(name, subNode); 272 } 273 } 274 275 ((Map)node).put(name, replacement); 276 return map; 277 } 278 279 280 public static void removeNode(Map<String, Object> map, List<Object> pathItems) { 281 Object node = map; 282 Object subNode = node; 283 Object name = null; 284 285 for (Object pathItem : pathItems) { 286 287 name = pathItem; 288 289 node = subNode; 290 subNode = getElement(node, pathItem); 291 292 if (subNode == null) return; 293 } 294 295 // remove leaf 296 if (node instanceof List) { 297 ((List) node).remove(name); 298 } else if (node instanceof Map) { 299 ((Map) node).remove(name); 300 } 301 } 302 303 private static Object getElement(Object node, Object pathItem) { 304 Object subNode; 305 if (node instanceof List && pathItem instanceof Integer) { 306 subNode = ((List) node).get((Integer) pathItem); 307 } else if (node instanceof Map && pathItem instanceof String) { 308 subNode = ((Map) node).get(pathItem); 309 } else 310 throw new IllegalArgumentException("Unexpected node/path: node " + node + " , path item " + pathItem); 311 return subNode; 312 } 313 314 public static boolean nodeExists(Object node, List<Object> pathItems) { 315 return getNode(node, pathItems) != null; 316 } 317 318 public static Object getNode(Object node, List<Object> pathItems) { 319 for (Object pathItem : pathItems) { 320 if (node == null) { 321 return null; 322 } else { 323 node = getElement(node, pathItem); 324 } 325 } 326 327 return node; 328 } 329 330 331}