/*
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
*
* Copyright 2011-2014 Peter Güttinger
*
*/
package ch.njol.skript.config;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.util.NonNullPair;
import ch.njol.util.StringUtils;
/**
* @author Peter Güttinger
*/
public abstract class Node {
@Nullable
protected String key;
protected String comment = "";
protected final int lineNum;
private final boolean debug;
@Nullable
protected SectionNode parent;
protected Config config;
// protected Node() {
// key = null;
// debug = false;
// lineNum = -1;
// SkriptLogger.setNode(this);
// }
protected Node(final Config c) {
key = null;
debug = false;
lineNum = -1;
config = c;
SkriptLogger.setNode(this);
}
protected Node(final String key, final SectionNode parent) {
this.key = key;
debug = false;
lineNum = -1;
this.parent = parent;
config = parent.getConfig();
SkriptLogger.setNode(this);
}
protected Node(final String key, final String comment, final SectionNode parent, final int lineNum) {
this.key = key;
assert comment.isEmpty() || comment.startsWith("#") : comment;
this.comment = comment;
debug = comment.equals("#DEBUG#");
this.lineNum = lineNum;
this.parent = parent;
config = parent.getConfig();
SkriptLogger.setNode(this);
}
// protected Node(final String key, final SectionNode parent, final ConfigReader r) {
// this(key, parent, r.getLine(), r.getLineNum());
// }
//
/**
* Key of this node. <tt>null</tt> for empty or invalid nodes, and the config's main node.
*/
@Nullable
public String getKey() {
return key;
}
public final Config getConfig() {
return config;
}
public void rename(final String newname) {
if (key == null)
throw new IllegalStateException("can't rename an anonymous node");
final String oldKey = key;
key = newname;
if (parent != null)
parent.renamed(this, oldKey);
}
public void move(final SectionNode newParent) {
final SectionNode p = parent;
if (p == null)
throw new IllegalStateException("can't move the main node");
p.remove(this);
newParent.add(this);
}
@SuppressWarnings("null")
private final static Pattern linePattern = Pattern.compile("^((?:[^#]|##)*)(\\s*#(?!#).*)$");
/**
* Splits a line into value and comment.
* <p>
* Whitespace is preserved (whitespace in front of the comment is added to the value), and any ## in the value are replaced by a single #. The comment is returned with a
* leading #, except if there is no comment in which case it will be the empty string.
*
* @param line
* @return A pair (value, comment).
*/
public final static NonNullPair<String, String> splitLine(final String line) {
final Matcher m = linePattern.matcher(line);
if (m.matches())
return new NonNullPair<String, String>("" + m.group(1).replace("##", "#"), "" + m.group(2));
return new NonNullPair<String, String>("" + line.replace("##", "#"), "");
}
@Nullable
protected String getComment() {
return comment;
}
int getLevel() {
int l = 0;
Node n = this;
while ((n = n.parent) != null) {
l++;
}
return Math.max(0, l - 1);
}
protected String getIndentation() {
return StringUtils.multiply(config.getIndentation(), getLevel());
}
/**
* @return String to save this node as. The correct indentation and the comment will be added automatically, as well as all '#'s will be escaped.
*/
abstract String save_i();
public final String save() {
return getIndentation() + save_i().replace("#", "##") + comment;
}
public void save(final PrintWriter w) {
w.println(save());
}
@Nullable
public SectionNode getParent() {
return parent;
}
/**
* Removes this node from its parent. Does nothing if this node does not have a parent node.
*/
public void remove() {
final SectionNode p = parent;
if (p == null)
return;
p.remove(this);
}
/**
* @return Original line of this node at the time it was loaded. <tt>-1</tt> if this node was created dynamically.
*/
public int getLine() {
return lineNum;
}
/**
* @return Whether this node does not hold information (i.e. is empty or invalid)
*/
public boolean isVoid() {
return this instanceof VoidNode;// || this instanceof ParseOptionNode;
}
// /**
// * get a node via path:to:the:node. relative paths are possible by starting with a ':'; a double colon '::' will go up a node.<br/>
// * selecting the n-th node can be done with #n.
// *
// * @param path
// * @return the node at the given path or null if the path is invalid
// */
// public Node getNode(final String path) {
// return getNode(path, false);
// }
//
// public Node getNode(String path, final boolean create) {
// Node n;
// if (path.startsWith(":")) {
// path = path.substring(1);
// n = this;
// } else {
// n = config.getMainNode();
// }
// for (final String s : path.split(":")) {
// if (s.isEmpty()) {
// n = n.getParent();
// if (n == null) {
// n = config.getMainNode();
// }
// continue;
// }
// if (!(n instanceof SectionNode)) {
// return null;
// }
// if (s.startsWith("#")) {
// int i = -1;
// try {
// i = Integer.parseInt(s.substring(1));
// } catch (final NumberFormatException e) {
// return null;
// }
// if (i <= 0 || i > ((SectionNode) n).getNodeList().size())
// return null;
// n = ((SectionNode) n).getNodeList().get(i - 1);
// } else {
// final Node oldn = n;
// n = ((SectionNode) n).get(s);
// if (n == null) {
// if (!create)
// return null;
// ((SectionNode) oldn).getNodeList().add(n = new SectionNode(s, (SectionNode) oldn, "", -1));
// }
// }
// }
// return n;
// }
/**
* returns information about this node which looks like the following:<br/>
* <code>node value #including comments (config.sk, line xyz)</code>
*/
@Override
public String toString() {
if (parent == null)
return config.getFileName();
return save_i() + comment + " (" + config.getFileName() + ", " + (lineNum == -1 ? "unknown line" : "line " + lineNum) + ")";
}
public boolean debug() {
return debug;
}
}