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