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