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) 2015 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 */ 040 041package org.dcm4che3.conf.core.api; 042 043import java.io.Serializable; 044import java.util.ArrayList; 045import java.util.Collections; 046import java.util.Iterator; 047import java.util.List; 048import java.util.regex.Matcher; 049import java.util.regex.Pattern; 050 051/** 052 * <b>CAUTION:</b> 053 * List indexes in the path representation start with 0. When converted to XPath, 1 is added as in XPath indexes start with 1. 054 * 055 * @author Roman K 056 */ 057public class Path implements Serializable { 058 059 private static final long serialVersionUID = 1069976968612802603L; 060 private static Pattern itemPattern = Pattern.compile("/(?<item>(\\\\/|[^/\\[\\]@\\*])*)"); 061 private static Pattern simplePathPattern = Pattern.compile("(" + itemPattern + ")*"); 062 063 public static final Path ROOT = new Path(); 064 065 private final List<Object> pathItems; 066 067 068 private transient String simpleEscapedXPath; 069 private transient String simpleEscapedPath; 070 071 public Path() { 072 pathItems = Collections.unmodifiableList(new ArrayList<Object>()); 073 } 074 075 public Path(Object... pathItems) { 076 077 ArrayList<Object> strings = new ArrayList<Object>(pathItems.length); 078 Collections.addAll(strings, pathItems); 079 this.pathItems = Collections.unmodifiableList(strings); 080 081 validate(); 082 } 083 084 public Path(List<?> pathItems) { 085 this.pathItems = Collections.unmodifiableList(new ArrayList<Object>(pathItems)); 086 validate(); 087 } 088 089 090 public Path(Iterator<Object> stringIterator) { 091 ArrayList<Object> strings = new ArrayList<Object>(); 092 while (stringIterator.hasNext()) 093 strings.add(stringIterator.next()); 094 this.pathItems = Collections.unmodifiableList(strings); 095 validate(); 096 } 097 098 private void validate() { 099 for (Object pathItem : this.pathItems) { 100 if (!((pathItem instanceof String) || (pathItem instanceof Integer))) 101 throw new IllegalArgumentException("Item '" + pathItem + "' is not allowed in path"); 102 } 103 } 104 105 106 @Override 107 public boolean equals(Object obj) { 108 109 if (obj == this) 110 return true; 111 if (!(obj instanceof Path)) 112 return false; 113 114 return pathItems.equals(((Path) obj).pathItems); 115 } 116 117 public List<Object> getPathItems() { 118 return pathItems; 119 } 120 121 /** 122 * @param indexFrom inclusive 123 * @param indexTo NOT inclusive 124 */ 125 public Path subPath(int indexFrom, int indexTo) { 126 127 ArrayList<Object> newItems = new ArrayList<Object>(); 128 while (indexFrom < indexTo) { 129 newItems.add(pathItems.get(indexFrom++)); 130 } 131 132 return new Path(newItems); 133 } 134 135 public int size() { 136 return getPathItems().size(); 137 } 138 139 public String toSimpleEscapedXPath() { 140 if (simpleEscapedXPath != null) 141 return simpleEscapedXPath; 142 143 String xpath = ""; 144 for (Object item : pathItems) { 145 146 if (item instanceof String) { 147 xpath += "/" + ((String) item).replace("/", "\\/"); 148 } else if (item instanceof Integer) { 149 // XPath indexes START WITH 1 150 xpath += "[" + ((Integer) item + 1) + "]"; 151 } else 152 throw new RuntimeException("Unexpected error"); 153 154 } 155 simpleEscapedXPath = xpath; 156 return xpath; 157 } 158 159 public String toSimpleEscapedPath() { 160 161 if (simpleEscapedPath != null) 162 return simpleEscapedPath; 163 164 String xpath = ""; 165 for (Object item : pathItems) { 166 xpath += "/"; 167 168 if (item instanceof Integer) { 169 xpath += "#"; 170 } 171 172 xpath += item 173 .toString() 174 .replace("/", "\\/") 175 .replace("#", "\\#"); 176 } 177 simpleEscapedPath = xpath; 178 179 return xpath; 180 } 181 182 183 @Override 184 public String toString() { 185 return toSimpleEscapedXPath(); 186 } 187 188 public static Path fromSimpleEscapedPath(String pathStr) { 189 Path path = fromSimpleEscapedPathOrNull(pathStr); 190 191 if (path == null) 192 throw new IllegalArgumentException("Simple path " + pathStr + " is invalid"); 193 return path; 194 } 195 196 public static Path fromSimpleEscapedPathOrNull(String path) { 197 198 if (!simplePathPattern.matcher(path).matches()) 199 return null; 200 201 Matcher matcher = itemPattern.matcher(path); 202 203 List<Object> list = new ArrayList<Object>(); 204 while (matcher.find()) { 205 String item = matcher.group("item"); 206 207 if (item.startsWith("#")) { 208 list.add(Integer.parseInt(item.substring(1))); 209 } else { 210 list.add(item 211 .replace("\\/", "/") 212 .replace("\\#", "#") 213 ); 214 } 215 216 } 217 218 return new Path(list); 219 } 220}