156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/* 256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc. 356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License"); 556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License. 656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at 756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0 956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software 1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS, 1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and 1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License. 1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.clearsilver.jsilver.data; 1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.EscapeMode; 2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.io.IOException; 2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.Map; 2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/** 2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * This is the basic implementation of the DataContext. It stores the root Data node and a stack of 2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Data objects that hold local variables. By definition, local variables are limited to single HDF 2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * names, with no '.' allowed. We use this to limit our search in the stack for the first occurence 2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * of the first chunk in the variable name. 2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class DefaultDataContext implements DataContext { 3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Root node of the Data structure used by the current context. 3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private final Data rootData; 3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Head of the linked list of local variables, starting with the newest variable created. 3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private LocalVariable head = null; 4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Indicates whether the renderer has pushed a new variable scope but no variable has been created 4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * yet. 4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private boolean newScope = false; 4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public DefaultDataContext(Data data) { 4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (data == null) { 5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new IllegalArgumentException("rootData is null"); 5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson this.rootData = data; 5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getRootData() { 5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return rootData; 5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Starts a new variable scope. It is illegal to call this twice in a row without declaring a 6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * local variable. 6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void pushVariableScope() { 6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (newScope) { 6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new IllegalStateException("PushVariableScope called twice with no " 6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson + "variables declared in between."); 6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson newScope = true; 7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Removes the current variable scope and references to the variables in it. It is illegal to call 7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * this more times than {@link #pushVariableScope} has been called. 7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void popVariableScope() { 7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (newScope) { 8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // We pushed but created no local variables. 8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson newScope = false; 8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Note, this will throw a NullPointerException if there is no scope to 8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // pop. 8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson head = head.nextScope; 8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void createLocalVariableByValue(String name, String value) { 9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson createLocalVariableByValue(name, value, EscapeMode.ESCAPE_NONE); 9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void createLocalVariableByValue(String name, String value, EscapeMode mode) { 9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable local = createLocalVariable(name); 9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.value = value; 9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.isPath = false; 9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.setEscapeMode(mode); 10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void createLocalVariableByValue(String name, String value, boolean isFirst, boolean isLast) { 10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable local = createLocalVariable(name); 10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.value = value; 10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.isPath = false; 10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.isFirst = isFirst; 10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.isLast = isLast; 10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void createLocalVariableByPath(String name, String path) { 11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable local = createLocalVariable(name); 11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.value = path; 11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.isPath = true; 11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private LocalVariable createLocalVariable(String name) { 11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (head == null && !newScope) { 12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new IllegalStateException("Must call pushVariableScope before " 12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson + "creating local variable."); 12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // First look for a local variable with the same name in the current scope 12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // and return that if it exists. If we don't do this then loops and each 12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // can cause the list of local variables to explode. 12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // 12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // We only look at the first local variable (head) if it is part of the 12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // current scope (we're not defining a new scope). Since each and loop 12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // variables are always in their own scope, there would only be one variable 13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // in the current scope if it was a reuse case. For macro calls (which are 13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // the only other way createLocalVariable is called multiple times in a 13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // single scope and thus head may not be the only local variable in the 13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // current scope) it is illegal to use the same argument name more than 13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // once. Therefore we don't need to worry about checking to see if the new 13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // local variable name matches beyond the first local variable in the 13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // current scope. 13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (!newScope && head != null && name.equals(head.name)) { 13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Clear out the fields that aren't set by the callers. 14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson head.isFirst = true; 14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson head.isLast = true; 14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson head.node = null; 14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return head; 14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable local = new LocalVariable(); 14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.name = name; 14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.next = head; 14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (newScope) { 15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.nextScope = head; 15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson newScope = false; 15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else if (head != null) { 15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.nextScope = head.nextScope; 15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson local.nextScope = null; 15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson head = local; 15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return local; 15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data findVariable(String name, boolean create) { 16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return findVariable(name, create, head); 16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public EscapeMode findVariableEscapeMode(String name) { 16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Data var = findVariable(name, false); 16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (var == null) { 17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return EscapeMode.ESCAPE_NONE; 17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return var.getEscapeMode(); 17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private Data findVariable(String name, boolean create, LocalVariable start) { 17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // When traversing the stack, we first look for the first chunk of the 17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // name. This is so we respect scope. If we are searching for 'foo.bar' 17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // and 'foo' is defined in many scopes, we should stop searching 18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // after the first time we find 'foo', even if that local variable does 18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // not have a child 'bar'. 18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson String firstChunk = name; 18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int dot = name.indexOf('.'); 18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (dot != -1) { 18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson firstChunk = name.substring(0, dot); 18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable curr = start; 18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson while (curr != null) { 19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (curr.name.equals(firstChunk)) { 19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (curr.isPath) { 19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // The local variable references another Data node, dereference it. 19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (curr.node == null) { 19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // We haven't resolved the path yet. Do it now and cache it if 19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // not null. Note we begin looking for the dereferenced in the next 19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // scope. 19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson curr.node = findVariable(curr.value, create, curr.nextScope); 19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (curr.node == null) { 20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Node does not exist. Any children won't either. 20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // We have a reference to the Data node directly. Use it. 20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (dot == -1) { 20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // This is the node we're interested in. 20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return curr.node; 20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (create) { 21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return curr.node.createChild(name.substring(dot + 1)); 21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return curr.node.getChild(name.substring(dot + 1)); 21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // This is a literal value local variable. It has no children, nor 21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // can it. We want to throw an error on creation of children. 21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (dot == -1) { 21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return curr; 22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (create) { 22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new IllegalStateException("Cannot create children of a " 22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson + "local literal variable"); 22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // No children. 22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson curr = curr.next; 23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (create) { 23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return rootData.createChild(name); 23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return rootData.getChild(name); 23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * This class holds the name and value/path of a local variable. Objects of this type should only 24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * be exposed outside of this class for value-based local variables. 24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p> 24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Fields are not private to avoid the performance overhead of hidden access methods used for 24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * outer classes to access private fields of inner classes. 24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private static class LocalVariable extends AbstractData { 24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Pointer to next LocalVariable in the list. 24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable next; 24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Pointer to the first LocalVariable in the next scope down. 25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson LocalVariable nextScope; 25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson String name; 25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson String value; 25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // True if value represents the path to another Data variable. 25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson boolean isPath; 25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Once the path resolves to a valid Data node, store it here to avoid 25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // refetching. 25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Data node = null; 25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Used only for loop local variables 26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson boolean isFirst = true; 26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson boolean isLast = true; 26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 26456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getName() { 26556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return name; 26656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 26756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getValue() { 26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return value; 27056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 27156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void setValue(String value) { 27356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson this.value = value; 27456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 27556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 27656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getFullPath() { 27756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return name; 27856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 27956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 28056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void setAttribute(String key, String value) { 28156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 28256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 28356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 28456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getAttribute(String key) { 28556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 28656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 28756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 28856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public boolean hasAttribute(String key) { 28956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return false; 29056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 29156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 29256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public int getAttributeCount() { 29356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return 0; 29456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 29556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 29656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Iterable<Map.Entry<String, String>> getAttributes() { 29756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 29856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 29956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 30056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getRoot() { 30156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 30256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 30356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 30456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getParent() { 30556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 30656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 30756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 30856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public boolean isFirstSibling() { 30956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return isFirst; 31056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 31156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 31256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public boolean isLastSibling() { 31356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return isLast; 31456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 31556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 31656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getNextSibling() { 31756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 31856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 31956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 32056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public int getChildCount() { 32156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return 0; 32256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 32356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 32456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Iterable<? extends Data> getChildren() { 32556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 32656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 32756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 32856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getChild(String path) { 32956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return null; 33056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 33156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 33256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data createChild(String path) { 33356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 33456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 33556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 33656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void removeTree(String path) { 33756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 33856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 33956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 34056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void setSymlink(String sourcePath, String destinationPath) { 34156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 34256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 34356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 34456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void setSymlink(String sourcePath, Data destination) { 34556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 34656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 34756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 34856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void setSymlink(Data symLink) { 34956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 35056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 35156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 35256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public Data getSymlink() { 35356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return this; 35456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 35556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 35656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void copy(String toPath, Data from) { 35756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 35856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 35956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 36056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void copy(Data from) { 36156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 36256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 36356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 36456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public String getValue(String path, String defaultValue) { 36556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new UnsupportedOperationException("Not allowed on local variables."); 36656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 36756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 36856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void write(Appendable out, int indent) throws IOException { 36956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson for (int i = 0; i < indent; i++) { 37056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson out.append(" "); 37156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 37256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson out.append(getName()).append(" = ").append(getValue()); 37356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 37456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 37556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson} 376