/* * Copyright 2015 Couchbase, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.couchbase.mock.subdoc; import com.google.gson.JsonElement; import java.util.ArrayList; import java.util.List; public class Path { final List<Component> components = new ArrayList<Component>(); public final static Component ROOT = new Component(); public final static int MAX_DEPTH = 32; private StringBuilder addComponent(StringBuilder sb, boolean isIndex) throws PathParseException, PathTooDeepException { if (components.size() == MAX_DEPTH-1) { throw new PathTooDeepException(); } components.add(new Component(sb.toString(), isIndex)); return new StringBuilder(); } private boolean prevWasIndex() { return !components.isEmpty() && components.get(components.size()-1).isIndex(); } public Path(String input) throws PathParseException, PathTooDeepException { boolean wantIndex = false; boolean inEscape = false; int numEscaped = 0; StringBuilder sb = new StringBuilder(); for (char s : input.toCharArray()) { if (s == '`') { if (inEscape) { inEscape = false; if (numEscaped == 0) { sb.append('`'); } } else { inEscape = true; numEscaped = 0; } continue; } if (inEscape) { numEscaped++; sb.append(s); continue; } if (s == '[') { if (wantIndex) { throw new PathParseException("Found nested brackets!"); } if (!sb.toString().isEmpty()) { sb = addComponent(sb, false); } wantIndex = true; } else if (s == ']') { if (!wantIndex) { throw new PathParseException("Found ] without opening ["); } sb = addComponent(sb, true); wantIndex = false; } else if (s == '.') { if (!prevWasIndex()) { sb = addComponent(sb, false); wantIndex = false; } } else { sb.append(s); } } if (wantIndex) { throw new PathParseException("Found unclosed ["); } String lastComp = sb.toString(); if (lastComp.isEmpty()) { if (!components.isEmpty() && !prevWasIndex()) { throw new PathParseException("Found empty non-root component!"); } } else { components.add(new Component(lastComp, false)); } } public Component get(int ix) { return components.get(ix); } public Component getLast() { if (components.isEmpty()) { return ROOT; } return components.get(components.size()-1); } public int size() { return components.size(); } public void validateComponentType(int ix, JsonElement parent) throws PathMismatchException { Component comp = components.get(ix); if (parent.isJsonPrimitive() || parent.isJsonNull()) { throw new PathMismatchException(); } if (comp.isIndex()) { if (!parent.isJsonArray()) { throw new PathMismatchException(); } } else { if (!parent.isJsonObject()) { throw new PathMismatchException(); } } } }