001/*- 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.parser.path; 021 022import org.apache.commons.lang3.Validate; 023 024import java.util.ArrayList; 025import java.util.List; 026import java.util.StringTokenizer; 027import java.util.stream.Collectors; 028 029import static org.apache.commons.lang3.StringUtils.isNotBlank; 030 031public class EncodeContextPath { 032 private final List<EncodeContextPathElement> myPath; 033 private final ArrayList<EncodeContextPathElement> myResourcePath = new ArrayList<>(10); 034 035 public EncodeContextPath() { 036 this(new ArrayList<>(10)); 037 } 038 039 public EncodeContextPath(String thePath) { 040 this(); 041 042 StringTokenizer tok = new StringTokenizer(thePath, "."); 043 boolean first = true; 044 while (tok.hasMoreTokens()) { 045 String next = tok.nextToken(); 046 if (first && next.equals("*")) { 047 getPath().add(new EncodeContextPathElement("*", true)); 048 } else if (isNotBlank(next)) { 049 getPath().add(new EncodeContextPathElement(next, Character.isUpperCase(next.charAt(0)))); 050 } 051 first = false; 052 } 053 } 054 055 public EncodeContextPath(List<EncodeContextPathElement> thePath) { 056 myPath = thePath; 057 } 058 059 @Override 060 public String toString() { 061 return myPath.stream().map(t -> t.toString()).collect(Collectors.joining(".")); 062 } 063 064 public List<EncodeContextPathElement> getPath() { 065 return myPath; 066 } 067 068 public EncodeContextPath getCurrentResourcePath() { 069 EncodeContextPath retVal = null; 070 for (int i = myPath.size() - 1; i >= 0; i--) { 071 if (myPath.get(i).isResource()) { 072 retVal = new EncodeContextPath(myPath.subList(i, myPath.size())); 073 break; 074 } 075 } 076 Validate.isTrue(retVal != null); 077 return retVal; 078 } 079 080 /** 081 * Add an element at the end of the path 082 */ 083 public void pushPath(String thePathElement, boolean theResource) { 084 assert isNotBlank(thePathElement); 085 assert !thePathElement.contains("."); 086 assert theResource ^ Character.isLowerCase(thePathElement.charAt(0)); 087 088 EncodeContextPathElement element = new EncodeContextPathElement(thePathElement, theResource); 089 getPath().add(element); 090 if (theResource) { 091 myResourcePath.add(element); 092 } 093 } 094 095 /** 096 * Remove the element at the end of the path 097 */ 098 public void popPath() { 099 EncodeContextPathElement removed = getPath().remove(getPath().size() - 1); 100 if (removed.isResource()) { 101 myResourcePath.remove(myResourcePath.size() - 1); 102 } 103 } 104 105 public ArrayList<EncodeContextPathElement> getResourcePath() { 106 return myResourcePath; 107 } 108 109 public String getLeafElementName() { 110 return getPath().get(getPath().size() - 1).getName(); 111 } 112 113 public String getLeafResourceName() { 114 return myResourcePath.get(myResourcePath.size() - 1).getName(); 115 } 116 117 public String getLeafResourcePathFirstField() { 118 String retVal = null; 119 for (int i = getPath().size() - 1; i >= 0; i--) { 120 if (getPath().get(i).isResource()) { 121 break; 122 } else { 123 retVal = getPath().get(i).getName(); 124 } 125 } 126 return retVal; 127 } 128 129 /** 130 * Tests and returns whether this path starts with {@literal theCurrentResourcePath} 131 * 132 * @param theCurrentResourcePath The path to test 133 * @param theAllowSymmmetrical If <code>true</code>, this method will return true if {@literal theCurrentResourcePath} starts with this path as well as testing whether this path starts with {@literal theCurrentResourcePath} 134 */ 135 public boolean startsWith(EncodeContextPath theCurrentResourcePath, boolean theAllowSymmmetrical) { 136 for (int i = 0; i < getPath().size(); i++) { 137 if (theCurrentResourcePath.getPath().size() == i) { 138 return true; 139 } 140 EncodeContextPathElement expected = getPath().get(i); 141 EncodeContextPathElement actual = theCurrentResourcePath.getPath().get(i); 142 if (!expected.matches(actual)) { 143 return false; 144 } 145 } 146 147 if (theAllowSymmmetrical) { 148 return true; 149 } 150 151 return getPath().size() == theCurrentResourcePath.getPath().size(); 152 } 153 154 public boolean equalsPath(String thePath) { 155 EncodeContextPath parsedPath = new EncodeContextPath(thePath); 156 return getPath().equals(parsedPath.getPath()); 157 } 158}