001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2025 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.jpa.patch;
021
022/**
023 * This class is deprecated; consider using ParsedFhirPath, which allows much greater flexibility
024 * in handling a fhirpath for various actions
025 *
026 * This class helps parse a FHIR path into its component parts for easier patch operation processing.
027 * It has 3 components:
028 *  - The last element name, which is the last element in the path (not including any list index or filter)
029 *  - The containing path, which is the prefix of the path up to the last element
030 *  - A flag indicating whether the path has a filter or index on the last element of the path, which indicates
031 *  that the path we are dealing is probably for a list element.
032 * Examples:
033 * 1. For path "Patient.identifier[2].system",
034 *   - the lastElementName is "system",
035 *   - the containingPath is "Patient.identifier[2]",
036 *   - and endsWithAFilterOrIndex flag is false
037 *
038 *  2. For path "Patient.identifier[2]" or for path "Patient.identifier.where('system'='sys1')"
039 *  - the lastElementName is "identifier",
040 *  - the containingPath is "Patient",
041 *  - and the endsWithAFilterOrIndex is true
042 */
043@Deprecated()
044public class ParsedPath {
045        private final String myLastElementName;
046        private final String myContainingPath;
047        private final boolean myEndsWithAFilterOrIndex;
048
049        public ParsedPath(String theLastElementName, String theContainingPath, boolean theEndsWithAFilterOrIndex) {
050                myLastElementName = theLastElementName;
051                myContainingPath = theContainingPath;
052                myEndsWithAFilterOrIndex = theEndsWithAFilterOrIndex;
053        }
054
055        /**
056         * returns the last element of the path
057         */
058        public String getLastElementName() {
059                return myLastElementName;
060        }
061
062        /**
063         * Returns the prefix of the path up to the last FHIR resource element
064         */
065        public String getContainingPath() {
066                return myContainingPath;
067        }
068
069        /**
070         * Returns whether the path has a filter or index on the last element of the path, which indicates
071         * that the path we are dealing is probably a list element.
072         */
073        public boolean getEndsWithAFilterOrIndex() {
074                return myEndsWithAFilterOrIndex;
075        }
076
077        public static ParsedPath parse(String path) {
078                String containingPath;
079                String elementName;
080                boolean endsWithAFilterOrIndex = false;
081
082                if (path.endsWith(")")) {
083                        // This is probably a filter, so we're probably dealing with a list
084                        endsWithAFilterOrIndex = true;
085                        int filterArgsIndex = path.lastIndexOf('('); // Let's hope there aren't nested parentheses
086                        int lastDotIndex = path.lastIndexOf(
087                                        '.', filterArgsIndex); // There might be a dot inside the parentheses, so look to the left of that
088                        int secondLastDotIndex = path.lastIndexOf('.', lastDotIndex - 1);
089                        containingPath = path.substring(0, secondLastDotIndex);
090                        elementName = path.substring(secondLastDotIndex + 1, lastDotIndex);
091                } else if (path.endsWith("]")) {
092                        // This is almost definitely a list
093                        endsWithAFilterOrIndex = true;
094                        int openBracketIndex = path.lastIndexOf('[');
095                        int lastDotIndex = path.lastIndexOf('.', openBracketIndex);
096                        containingPath = path.substring(0, lastDotIndex);
097                        elementName = path.substring(lastDotIndex + 1, openBracketIndex);
098                } else {
099                        int lastDot = path.lastIndexOf(".");
100                        containingPath = path.substring(0, lastDot);
101                        elementName = path.substring(lastDot + 1);
102                }
103
104                return new ParsedPath(elementName, containingPath, endsWithAFilterOrIndex);
105        }
106}