/* * ToroDB * Copyright © 2014 8Kdata Technology (www.8kdata.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.torodb.d2r.model; import com.torodb.core.TableRef; import com.torodb.core.TableRefFactory; //TODO: Add constraint annotations and asserts public class PathStack { private final TableRefFactory tableRefFactory; private PathInfo top = null; public enum PathNodeType { Field, Object, Array, Idx } public PathStack(TableRefFactory tableRefFactory) { this.tableRefFactory = tableRefFactory; this.top = new PathField("", null); } public PathInfo peek() { return top; } public PathInfo pop() { PathInfo topper = top; top = top.getParent(); return topper; } public void pushField(String name) { top = top.appendField(name); } public void pushObject(DocPartRowImpl rowInfo) { top = top.appendObject(rowInfo); } public void pushArray() { if (top == null) { throw new IllegalArgumentException("Building an array on root document"); } top = top.appendArray(); } public void pushArrayIdx(int idx) { if (top == null) { throw new IllegalArgumentException("Building an array index on root document"); } else if (top.getNodeType() != PathNodeType.Array) { throw new IllegalArgumentException("Building an array index on document"); } top = ((PathArray) top).appendIdx(idx); } public void pushArrayIdx(int idx, DocPartRowImpl rowInfo) { if (top == null) { throw new IllegalArgumentException("Building an array index on root document"); } else if (top.getNodeType() != PathNodeType.Array) { throw new IllegalArgumentException("Building an array index on document"); } top = ((PathArray) top).appendIdx(idx, rowInfo); } public abstract class PathInfo { protected PathInfo parent; protected TableRef tableRef; private PathInfo(PathInfo parent) { this.parent = parent; } PathObject appendObject(DocPartRowImpl rowInfo) { return new PathObject(this, rowInfo); } PathField appendField(String name) { return new PathField(name, this); } PathArray appendArray() { return new PathArray(1, this); } public TableRef getTableRef() { return tableRef; } public PathInfo getParent() { return this.parent; } @Override public int hashCode() { return this.getPath().hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof PathInfo)) { return false; } PathInfo other = (PathInfo) obj; return this.getPath().equals(other.getPath()); } public boolean is(PathNodeType type) { return getNodeType().equals(type); } public abstract DocPartRowImpl findParentRowInfo(); public abstract String getPath(); public abstract PathNodeType getNodeType(); } public class PathField extends PathInfo { private String fieldName; private String path; private PathField(String fieldName, PathInfo parent) { super(parent); this.fieldName = fieldName; this.path = calcPath(); if (parent == null) { this.tableRef = tableRefFactory.createRoot(); } else { this.tableRef = tableRefFactory.createChild(parent.getTableRef(), fieldName); } } private String calcPath() { if (parent == null || parent.getPath().length() == 0) { return getFieldName(); } return parent.getPath() + "." + getFieldName(); } public PathObject getParentObject() { return (PathObject) parent; } @Override public String getPath() { return path; } public String getFieldName() { return fieldName; } @Override public PathNodeType getNodeType() { return PathNodeType.Field; } @Override public String toString() { if (parent == null || parent.getPath().length() == 0) { return getFieldName(); } return parent.toString() + "." + getFieldName(); } @Override public DocPartRowImpl findParentRowInfo() { if (parent != null) { return parent.findParentRowInfo(); } return null; } } public class PathObject extends PathInfo { private DocPartRowImpl rowInfo; private PathObject(PathInfo parent, DocPartRowImpl rowInfo) { super(parent); this.rowInfo = rowInfo; this.tableRef = parent.getTableRef(); } @Override public String getPath() { return parent.getPath(); } @Override public PathNodeType getNodeType() { return PathNodeType.Object; } @Override public String toString() { return parent.toString(); } public DocPartRowImpl findParentRowInfo() { return rowInfo; } } public class PathArray extends PathInfo { private int dimension; private String path; private PathArray(int dimension, PathInfo parent) { super(parent); this.dimension = dimension; this.path = calcPath(); if (dimension == 1) { this.tableRef = parent.getTableRef(); } else { this.tableRef = tableRefFactory.createChild(parent.tableRef, dimension); } } PathArrayIdx appendIdx(int idx) { return new PathArrayIdx(idx, this); } PathArrayIdx appendIdx(int idx, DocPartRowImpl rowInfo) { return new PathArrayIdx(idx, this, rowInfo); } @Override public String getPath() { return path; } private String calcPath() { if (dimension == 1) { return parent.getPath(); } PathInfo noArray = parent; while (noArray.getNodeType() == PathNodeType.Array || noArray.getNodeType() == PathNodeType.Idx) { noArray = noArray.getParent(); } return noArray.getPath() + "$" + dimension; } @Override public String toString() { return parent.toString(); } @Override public PathNodeType getNodeType() { return PathNodeType.Array; } public DocPartRowImpl findParentRowInfo() { if (parent != null) { return parent.findParentRowInfo(); } return null; } } // TODO maybe create to types: with and without rowinfo public class PathArrayIdx extends PathInfo { private int idx; private DocPartRowImpl rowInfo; private PathArrayIdx(int idx, PathInfo parent) { this(idx, parent, null); } private PathArrayIdx(int idx, PathInfo parent, DocPartRowImpl rowInfo) { super(parent); assert parent.getNodeType() == PathNodeType.Array; this.idx = idx; this.rowInfo = rowInfo; this.tableRef = parent.tableRef; } PathArray appendArray() { return new PathArray(((PathArray) parent).dimension + 1, this); } @Override public String getPath() { return parent.getPath(); } @Override public PathNodeType getNodeType() { return PathNodeType.Idx; } @Override public String toString() { return parent.toString() + "[]"; } public int getIdx() { return idx; } public DocPartRowImpl findParentRowInfo() { if (rowInfo != null) { return rowInfo; } if (parent != null) { return parent.findParentRowInfo(); } return null; } } }