		@Override
		public ParseTree optimizeDynamic(Target t, Environment env, List<ParseTree> children, FileOptions fileOptions) throws ConfigCompileException, ConfigRuntimeException {
			if(children.size() > 1 && children.get(1).getData() instanceof CFunction
					&& new StringHandling.sconcat().getName().equals(children.get(1).getData().val())) {
				//This is the brace/case/default usage of switch, probably. We need
				//to refactor the data into the old switch format.
				List<ParseTree> newChildren = new ArrayList<>();
				newChildren.add(children.get(0)); //Initial child
				List<ParseTree> c = children.get(1).getChildren();
				List<ParseTree> lastCodeBlock = new ArrayList<>();
				CArray conditions = new CArray(t);
				boolean inCase = false;
				boolean inDefault = false;
				for(int i = 0; i < c.size(); i++) {
					//Need up to a 2 lookahead
					ParseTree c1 = c.get(i);
					ParseTree c2 = null;
					if(i + 1 < c.size()) {
						c2 = c.get(i + 1);
					}
					if(CKeyword.isKeyword(c1, "case")) {
						//If this is a case AND the next one is
						//a label, this is a case.
						if(c2 != null && c2.getData() instanceof CLabel) {
							if(inDefault) {
								//Default must come last
								throw new ConfigCompileException("Unexpected case; the default case must come last.", t);
							}
							if(lastCodeBlock.size() > 0) {
								//Ok, need to push some stuff on to the new children
								newChildren.add(new ParseTree(conditions, c2.getFileOptions()));
								conditions = new CArray(t);
								ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), c2.getFileOptions());
								for(ParseTree line : lastCodeBlock) {
									codeBlock.addChild(line);
								}
								if(codeBlock.getChildren().size() == 1) {
									codeBlock = codeBlock.getChildAt(0);
								}
								newChildren.add(codeBlock);
								lastCodeBlock = new ArrayList<>();
							}
							//Yes, it is. Now we also have to look ahead for
							//other cases, because
							//case 1:
							//case 2:
							//	code()
							//would be turned into array(1, 2), code() in the
							//old style.
							conditions.push(((CLabel) c2.getData()).cVal(), t);
							inCase = true;
							i++;
							continue;
						}
					}
					if(c1.getData() instanceof CLabel && CKeyword.isKeyword(((CLabel) c1.getData()).cVal(), "default")) {
						//Default case
						if(lastCodeBlock.size() > 0) {
							//Ok, need to push some stuff on to the new children
							newChildren.add(new ParseTree(conditions, c1.getFileOptions()));
							conditions = new CArray(t);
							ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), c1.getFileOptions());
							for(ParseTree line : lastCodeBlock) {
								codeBlock.addChild(line);
							}
							if(codeBlock.getChildren().size() == 1) {
								codeBlock = codeBlock.getChildAt(0);
							}
							newChildren.add(codeBlock);
							lastCodeBlock = new ArrayList<>();
						} else if(conditions.size() > 0) {
							//Special case where they have
							//case 0:
							//default:
							//	code();
							//This causes there to be conditions, but no code,
							//which throws off the argument length. In actuality,
							//we can simply throw out the conditions, because
							//this block of code will run if 0 or the default is
							//hit, and if 0 is the condition provided, it would
							//work the same if it weren't specified at all.
							conditions = new CArray(t);
						}
						inDefault = true;
						continue;
					}

					//Loop forward until we get to the next case
					if(inCase || inDefault) {
						lastCodeBlock.add(c1);
					}
				}
				if(conditions.size() > 0) {
					newChildren.add(new ParseTree(conditions, children.get(0).getFileOptions()));
				}
				if(lastCodeBlock.size() > 0) {
					ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), lastCodeBlock.get(0).getFileOptions());
					for(ParseTree line : lastCodeBlock) {
						codeBlock.addChild(line);
					}
					if(codeBlock.getChildren().size() == 1) {
						codeBlock = codeBlock.getChildAt(0);
					}
					newChildren.add(codeBlock);
				}
				children.clear();
				children.addAll(newChildren);
			}

			//Loop through all the conditions and make sure each is unique. Also
			//make sure that each value is not dynamic.
			String notConstant = "Cases for a switch statement must be constant, not variable";
			String alreadyContains = "The switch statement already contains a case for this value, remove the duplicate value";
			final equals equals = new equals();
			Set<Mixed> values = new TreeSet<>((Mixed t1, Mixed t2) -> {
				if(equals.exec(Target.UNKNOWN, null, t1, t2).getBoolean()) {
					return 0;
				} else {
					return t1.val().compareTo(t2.val());
				}
			});
			final boolean hasDefaultCase = (children.size() & 0b00000001) == 0; // size % 2 == 0 -> Even number means there is a default.
			for(int i = 1; i < children.size(); i += 2) {
				if(hasDefaultCase && i == children.size() - 1) {
					// This is the default case code. Stop checking here.
					break;
				}
				//To standardize the rest of the code (and to optimize), go ahead and resolve array()
				if(children.get(i).getData() instanceof CFunction
						&& new DataHandling.array().getName().equals(children.get(i).getData().val())) {
					CArray data = new CArray(t);
					for(ParseTree child : children.get(i).getChildren()) {
						if(Construct.IsDynamicHelper(child.getData())) {
							throw new ConfigCompileException(notConstant, child.getTarget());
						}
						data.push(child.getData(), t);
					}
					children.set(i, new ParseTree(data, children.get(i).getFileOptions()));
				}
				//Now we validate that the values are constant and non-repeating.
				if(children.get(i).getData() instanceof CArray) {
					List<Mixed> list = ((CArray) children.get(i).getData()).asList();
					for(Mixed c : list) {
						if(c instanceof CSlice) {
							for(Mixed cc : ((CSlice) c).asList()) {
								if(values.contains(cc)) {
									throw new ConfigCompileException(alreadyContains, cc.getTarget());
								}
								values.add(cc);
							}
						} else {
							if(Construct.IsDynamicHelper(c)) {
								throw new ConfigCompileException(notConstant, c.getTarget());
							}
							if(values.contains(c)) {
								throw new ConfigCompileException(alreadyContains, c.getTarget());
							}
							values.add(c);
						}
					}
				} else {
					Mixed c = children.get(i).getData();
					if(Construct.IsDynamicHelper(c)) {
						throw new ConfigCompileException(notConstant, c.getTarget());
					}
					if(values.contains(c)) {
						throw new ConfigCompileException(alreadyContains, c.getTarget());
					}
					values.add(c);
				}
			}

			if((children.size() > 3 || (children.size() > 1 && children.get(1).getData() instanceof CArray))
					//No point in doing this optimization if there are only 3 args and the case is flat.
					//Also, doing this check prevents an inifinite loop during optimization.
					&& (children.size() > 0 && !Construct.IsDynamicHelper(children.get(0).getData()))) {
				ParseTree toReturn = null;
				//The item passed in is constant (or has otherwise been made constant)
				//so we can go ahead and condense this down to the single code path
				//in the switch.
				for(int i = 1; i < children.size(); i += 2) {
					Mixed data = children.get(i).getData();

					if(!(data instanceof CArray) || data instanceof CSlice) {
						//Put it in an array to make the rest of this parsing easier.
						data = new CArray(t);
						((CArray) data).push(children.get(i).getData(), t);
					}
					for(Mixed value : ((CArray) data).asList()) {
						if(value instanceof CSlice) {
							long rangeLeft = ((CSlice) value).getStart();
							long rangeRight = ((CSlice) value).getFinish();
							if(children.get(0).getData() instanceof CInt) {
								long v = Static.getInt(children.get(0).getData(), t);
								if((rangeLeft < rangeRight && v >= rangeLeft && v <= rangeRight)
										|| (rangeLeft > rangeRight && v >= rangeRight && v <= rangeLeft)
										|| (rangeLeft == rangeRight && v == rangeLeft)) {
									toReturn = children.get(i + 1);
									break;
								}
							}
						} else if(equals.exec(t, null, children.get(0).getData(), value).getBoolean()) {
							toReturn = children.get(i + 1);
							break;
						}
					}
				}
				//None of the values match. Return the default case, if it exists, or remove the switch entirely
				//if it doesn't.
				if(toReturn == null) {
					if(children.size() % 2 == 0) {
						toReturn = children.get(children.size() - 1);
					} else {
						return Optimizable.REMOVE_ME;
					}
				}
				//Unfortunately, we can't totally remove this, because otherwise break()s in the code
				//will go unchecked, so we need to keep switch in the code somehow. To make it easy though,
				//we'll make the most efficient switch we can.
				ParseTree ret = new ParseTree(new CFunction(new _switch().getName(), t), fileOptions);
				ret.addChild(new ParseTree(new CInt(1, t), fileOptions));
				ret.addChild(new ParseTree(new CInt(1, t), fileOptions));
				ret.addChild(toReturn);
				return ret;
			}
			return null;
		}

		@Override
		public ParseTree optimizeDynamic(Target t, Environment env, List<ParseTree> children, FileOptions fileOptions) throws ConfigCompileException, ConfigRuntimeException {
			if(children.size() > 1 && children.get(1).getData() instanceof CFunction
					&& new StringHandling.sconcat().getName().equals(children.get(1).getData().val())) {
				//This is the brace/case/default usage of switch, probably. We need
				//to refactor the data into the old switch format.
				List<ParseTree> newChildren = new ArrayList<>();
				newChildren.add(children.get(0)); //Initial child
				List<ParseTree> c = children.get(1).getChildren();
				List<ParseTree> lastCodeBlock = new ArrayList<>();
				CArray conditions = new CArray(t);
				boolean inCase = false;
				boolean inDefault = false;
				for(int i = 0; i < c.size(); i++) {
					//Need up to a 2 lookahead
					ParseTree c1 = c.get(i);
					ParseTree c2 = null;
					if(i + 1 < c.size()) {
						c2 = c.get(i + 1);
					}
					if(CKeyword.isKeyword(c1, "case")) {
						//If this is a case AND the next one is
						//a label, this is a case.
						if(c2 != null && c2.getData() instanceof CLabel) {
							if(inDefault) {
								//Default must come last
								throw new ConfigCompileException("Unexpected case; the default case must come last.", t);
							}
							if(lastCodeBlock.size() > 0) {
								//Ok, need to push some stuff on to the new children
								newChildren.add(new ParseTree(conditions, c2.getFileOptions()));
								conditions = new CArray(t);
								ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), c2.getFileOptions());
								for(ParseTree line : lastCodeBlock) {
									codeBlock.addChild(line);
								}
								if(codeBlock.getChildren().size() == 1) {
									codeBlock = codeBlock.getChildAt(0);
								}
								newChildren.add(codeBlock);
								lastCodeBlock = new ArrayList<>();
							}
							//Yes, it is. Now we also have to look ahead for
							//other cases, because
							//case 1:
							//case 2:
							//	code()
							//would be turned into array(1, 2), code() in the
							//old style.
							conditions.push(((CLabel) c2.getData()).cVal(), t);
							inCase = true;
							i++;
							continue;
						}
					}
					if(c1.getData() instanceof CLabel && CKeyword.isKeyword(((CLabel) c1.getData()).cVal(), "default")) {
						//Default case
						if(lastCodeBlock.size() > 0) {
							//Ok, need to push some stuff on to the new children
							newChildren.add(new ParseTree(conditions, c1.getFileOptions()));
							conditions = new CArray(t);
							ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), c1.getFileOptions());
							for(ParseTree line : lastCodeBlock) {
								codeBlock.addChild(line);
							}
							if(codeBlock.getChildren().size() == 1) {
								codeBlock = codeBlock.getChildAt(0);
							}
							newChildren.add(codeBlock);
							lastCodeBlock = new ArrayList<>();
						} else if(conditions.size() > 0) {
							//Special case where they have
							//case 0:
							//default:
							//	code();
							//This causes there to be conditions, but no code,
							//which throws off the argument length. In actuality,
							//we can simply throw out the conditions, because
							//this block of code will run if 0 or the default is
							//hit, and if 0 is the condition provided, it would
							//work the same if it weren't specified at all.
							conditions = new CArray(t);
						}
						inDefault = true;
						continue;
					}

					//Loop forward until we get to the next case
					if(inCase || inDefault) {
						lastCodeBlock.add(c1);
					}
				}
				if(conditions.size() > 0) {
					newChildren.add(new ParseTree(conditions, children.get(0).getFileOptions()));
				}
				if(lastCodeBlock.size() > 0) {
					ParseTree codeBlock = new ParseTree(new CFunction(new StringHandling.sconcat().getName(), t), lastCodeBlock.get(0).getFileOptions());
					for(ParseTree line : lastCodeBlock) {
						codeBlock.addChild(line);
					}
					if(codeBlock.getChildren().size() == 1) {
						codeBlock = codeBlock.getChildAt(0);
					}
					newChildren.add(codeBlock);
				}
				children.clear();
				children.addAll(newChildren);
			}

			//Loop through all the conditions and make sure each is unique. Also
			//make sure that each value is not dynamic.
			String notConstant = "Cases for a switch statement must be constant, not variable";
			String alreadyContains = "The switch statement already contains a case for this value, remove the duplicate value";
			final equals equals = new equals();
			Set<Mixed> values = new TreeSet<>(new Comparator<Mixed>() {

				@Override
				public int compare(Mixed t, Mixed t1) {
					if(equals.exec(Target.UNKNOWN, null, t, t1).getBoolean()) {
						return 0;
					} else {
						return t.val().compareTo(t1.val());
					}
				}
			});
			final boolean hasDefaultCase = (children.size() & 0b00000001) == 0; // size % 2 == 0 -> Even number means there is a default.
			for(int i = 1; i < children.size(); i += 2) {
				if(hasDefaultCase && i == children.size() - 1) {
					// This is the default case code. Stop checking here.
					break;
				}
				//To standardize the rest of the code (and to optimize), go ahead and resolve array()
				if(children.get(i).getData() instanceof CFunction
						&& new DataHandling.array().getName().equals(children.get(i).getData().val())) {
					CArray data = new CArray(t);
					for(ParseTree child : children.get(i).getChildren()) {
						if(Construct.IsDynamicHelper(child.getData())) {
							throw new ConfigCompileException(notConstant, child.getTarget());
						}
						data.push(child.getData(), t);
					}
					children.set(i, new ParseTree(data, children.get(i).getFileOptions()));
				}
				//Now we validate that the values are constant and non-repeating.
				if(children.get(i).getData() instanceof CArray) {
					List<Mixed> list = ((CArray) children.get(i).getData()).asList();
					for(Mixed c : list) {
						if(c instanceof CSlice) {
							for(Mixed cc : ((CSlice) c).asList()) {
								if(values.contains(cc)) {
									throw new ConfigCompileException(alreadyContains, cc.getTarget());
								}
								values.add(cc);
							}
						} else {
							if(Construct.IsDynamicHelper(c)) {
								throw new ConfigCompileException(notConstant, c.getTarget());
							}
							if(values.contains(c)) {
								throw new ConfigCompileException(alreadyContains, c.getTarget());
							}
							values.add(c);
						}
					}
				} else {
					Mixed c = children.get(i).getData();
					if(Construct.IsDynamicHelper(c)) {
						throw new ConfigCompileException(notConstant, c.getTarget());
					}
					if(values.contains(c)) {
						throw new ConfigCompileException(alreadyContains, c.getTarget());
					}
					values.add(c);
				}
			}

			if((children.size() > 3 || (children.size() > 1 && children.get(1).getData() instanceof CArray))
					//No point in doing this optimization if there are only 3 args and the case is flat.
					//Also, doing this check prevents an inifinite loop during optimization.
					&& (children.size() > 0 && !Construct.IsDynamicHelper(children.get(0).getData()))) {
				ParseTree toReturn = null;
				//The item passed in is constant (or has otherwise been made constant)
				//so we can go ahead and condense this down to the single code path
				//in the switch.
				for(int i = 1; i < children.size(); i += 2) {
					Mixed data = children.get(i).getData();

					if(!(data instanceof CArray) || data instanceof CSlice) {
						//Put it in an array to make the rest of this parsing easier.
						data = new CArray(t);
						((CArray) data).push(children.get(i).getData(), t);
					}
					for(Mixed value : ((CArray) data).asList()) {
						if(value instanceof CSlice) {
							long rangeLeft = ((CSlice) value).getStart();
							long rangeRight = ((CSlice) value).getFinish();
							if(children.get(0).getData() instanceof CInt) {
								long v = Static.getInt(children.get(0).getData(), t);
								if((rangeLeft < rangeRight && v >= rangeLeft && v <= rangeRight)
										|| (rangeLeft > rangeRight && v >= rangeRight && v <= rangeLeft)
										|| (rangeLeft == rangeRight && v == rangeLeft)) {
									toReturn = children.get(i + 1);
									break;
								}
							}
						} else if(equals.exec(t, null, children.get(0).getData(), value).getBoolean()) {
							toReturn = children.get(i + 1);
							break;
						}
					}
				}
				//None of the values match. Return the default case, if it exists, or remove the switch entirely
				//if it doesn't.
				if(toReturn == null) {
					if(children.size() % 2 == 0) {
						toReturn = children.get(children.size() - 1);
					} else {
						return Optimizable.REMOVE_ME;
					}
				}
				//Unfortunately, we can't totally remove this, because otherwise break()s in the code
				//will go unchecked, so we need to keep switch in the code somehow. To make it easy though,
				//we'll make the most efficient switch we can.
				ParseTree ret = new ParseTree(new CFunction(new _switch().getName(), t), fileOptions);
				ret.addChild(new ParseTree(new CInt(1, t), fileOptions));
				ret.addChild(new ParseTree(new CInt(1, t), fileOptions));
				ret.addChild(toReturn);
				return ret;
			}
			return null;
		}

Replace Anonymous With Lambda com.laytonsmith.core.functions.BasicLogic._switch.optimizeDynamic.values.new TreeSet<>.new Comparator<Mixed> with (t1, t2) -> {if(equals.exec(Target.UNKNOWN,null,t1,t2).getBoolean()){return 0; in method public optimizeDynamic(t Target, env Environment, children List<ParseTree>, fileOptions FileOptions) : ParseTree from class com.laytonsmith.core.functions.BasicLogic._switch


	public void pushVariableStack() {
		knownVars.push(new HashSet<>());
	}

	public void pushVariableStack() {
		knownVars.push(new HashSet<String>());
	}

Replace Generic With Diamond HashSet<String> with HashSet<> in method public pushVariableStack() : void from class com.laytonsmith.core.compiler.CompilerEnvironment


	public List<ParseTree> getIncludes() {
		return new ArrayList<>(includes);
	}

	public List<ParseTree> getIncludes() {
		return new ArrayList<ParseTree>(includes);
	}

Replace Generic With Diamond ArrayList<ParseTree> with ArrayList<> in method public getIncludes() : List<ParseTree> from class com.laytonsmith.core.compiler.CompilerEnvironment


/**
	 * Creates a new smart comment.
	 *
	 * @param comment The comment to be parsed
	 * @param replacements This is used to replace embedded annotations with some other text. For instance, if the
	 * comment contained {
	 * @ code myCode }, (minus spaces) it may be used to return "myCode". By default, if a
	 * particular embedded annotation has no handler, the embedded text is simply used as is.
	 */
	public SmartComment(String comment, Map<String, Replacement> replacements) {
		if(replacements == null) {
			replacements = new HashMap<>();
		}

		//Remove the @ at the beginning, if present.
		for(String key : replacements.keySet()) {
			rplcmnt.put(key.replaceFirst("@", ""), replacements.get(key));
		}

		comment = comment.trim();
		if(comment.startsWith("/**")) {
			comment = comment.substring(3);
		}
		if(comment.endsWith("*/")) {
			comment = comment.substring(0, comment.length() - 2);
		}
		String[] lines = comment.split("\n|\r\n|\n\r");
		StringBuilder b = new StringBuilder();
		for(String line : lines) {
			line = line.replaceFirst(LINE_START, "");
			b.append("\n").append(line);
		}

		raw = replaceEmbedded(b.toString().trim());

		String[] words = raw.split(" |\n");
		StringBuilder buffer = new StringBuilder();
		String lastAnnotation = null;
		int annotationIndex = -1;
		for(String word : words) {
			if(ANNOTATION.matcher(word).matches()) {
				if(annotationIndex == -1) {
					Matcher m = ANNOTATION.matcher(raw);
					m.find();
					annotationIndex = m.start();
				}
				processBuffer(lastAnnotation, buffer.toString());
				lastAnnotation = word;
				buffer = new StringBuilder();
			} else {
				buffer.append(" ").append(word);
			}
		}
		processBuffer(lastAnnotation, buffer.toString());
		if(annotationIndex == -1) {
			body = raw;
		} else {
			body = raw.substring(0, annotationIndex).trim();
		}
	}

/**
	 * Creates a new smart comment.
	 *
	 * @param comment The comment to be parsed
	 * @param replacements This is used to replace embedded annotations with some other text. For instance, if the
	 * comment contained {
	 * @ code myCode }, (minus spaces) it may be used to return "myCode". By default, if a
	 * particular embedded annotation has no handler, the embedded text is simply used as is.
	 */
	public SmartComment(String comment, Map<String, Replacement> replacements) {
		if(replacements == null) {
			replacements = new HashMap<String, Replacement>();
		}

		//Remove the @ at the beginning, if present.
		for(String key : replacements.keySet()) {
			rplcmnt.put(key.replaceFirst("@", ""), replacements.get(key));
		}

		comment = comment.trim();
		if(comment.startsWith("/**")) {
			comment = comment.substring(3);
		}
		if(comment.endsWith("*/")) {
			comment = comment.substring(0, comment.length() - 2);
		}
		String[] lines = comment.split("\n|\r\n|\n\r");
		StringBuilder b = new StringBuilder();
		for(String line : lines) {
			line = line.replaceFirst(LINE_START, "");
			b.append("\n").append(line);
		}

		raw = replaceEmbedded(b.toString().trim());

		String[] words = raw.split(" |\n");
		StringBuilder buffer = new StringBuilder();
		String lastAnnotation = null;
		int annotationIndex = -1;
		for(String word : words) {
			if(ANNOTATION.matcher(word).matches()) {
				if(annotationIndex == -1) {
					Matcher m = ANNOTATION.matcher(raw);
					m.find();
					annotationIndex = m.start();
				}
				processBuffer(lastAnnotation, buffer.toString());
				lastAnnotation = word;
				buffer = new StringBuilder();
			} else {
				buffer.append(" ").append(word);
			}
		}
		processBuffer(lastAnnotation, buffer.toString());
		if(annotationIndex == -1) {
			body = raw;
		} else {
			body = raw.substring(0, annotationIndex).trim();
		}
	}

Replace Generic With Diamond HashMap<String,Replacement> with HashMap<> in method public SmartComment(comment String, replacements Map<String,Replacement>) from class com.laytonsmith.PureUtilities.SmartComment


	private void addAnnotation(String name, String value) {
		if(!annotations.containsKey(name)) {
			annotations.put(name, new ArrayList<>());
		}
		annotations.get(name).add(value);
	}

	private void addAnnotation(String name, String value) {
		if(!annotations.containsKey(name)) {
			annotations.put(name, new ArrayList<String>());
		}
		annotations.get(name).add(value);
	}

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method private addAnnotation(name String, value String) : void from class com.laytonsmith.PureUtilities.SmartComment


	/**
	 * Gets a list of annotation values for the comment block.
	 *
	 * @param annotation
	 * @return
	 */
	public List<String> getAnnotations(String annotation) {
		if(!annotation.startsWith("@")) {
			annotation = "@" + annotation;
		}
		return new ArrayList<>(annotations.get(annotation));
	}

	/**
	 * Gets a list of annotation values for the comment block.
	 *
	 * @param annotation
	 * @return
	 */
	public List<String> getAnnotations(String annotation) {
		if(!annotation.startsWith("@")) {
			annotation = "@" + annotation;
		}
		return new ArrayList<String>(annotations.get(annotation));
	}

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method public getAnnotations(annotation String) : List<String> from class com.laytonsmith.PureUtilities.SmartComment


	public VariableStack() {
		vars = new Stack<>();
		pushScope();
	}

	public VariableStack() {
		vars = new Stack<Map<String, Construct>>();
		pushScope();
	}

Replace Generic With Diamond Stack<Map<String,Construct>> with Stack<> in method public VariableStack() from class com.laytonsmith.core.compiler.VariableStack


	public void pushScope() {
		vars.push(new HashMap<>());
	}

	public void pushScope() {
		vars.push(new HashMap<String, Construct>());
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public pushScope() : void from class com.laytonsmith.core.compiler.VariableStack


	private Construct get(String name, Target t) {
		List<Map<String, Construct>> varsReversed = new ArrayList<>();
		Collections.reverse(varsReversed);
		for(Map<String, Construct> map : varsReversed) {
			if(map.containsKey(name)) {
				return map.get(name);
			}
		}
		return new CString("", t);
	}

	private Construct get(String name, Target t) {
		List<Map<String, Construct>> varsReversed = new ArrayList<Map<String, Construct>>();
		Collections.reverse(varsReversed);
		for(Map<String, Construct> map : varsReversed) {
			if(map.containsKey(name)) {
				return map.get(name);
			}
		}
		return new CString("", t);
	}

Replace Generic With Diamond ArrayList<Map<String,Construct>> with ArrayList<> in method private get(name String, t Target) : Construct from class com.laytonsmith.core.compiler.VariableStack


	@Override
	public Set<String[]> getNamespace(String[] namespace) throws DataSourceException {
		Set<String[]> list = new HashSet<>();
		String ns = StringUtils.Join(namespace, ".");
		for(String key : stringKeySet(namespace)) {
			if("".equals(ns) //Blank string; this means they want it to always match.
					|| key.matches(Pattern.quote(ns) + "(?:$|\\..*)")) {
				String[] split = key.split("\\.");
				list.add(split);
			}
		}
		return list;
	}

	@Override
	public Set<String[]> getNamespace(String[] namespace) throws DataSourceException {
		Set<String[]> list = new HashSet<String[]>();
		String ns = StringUtils.Join(namespace, ".");
		for(String key : stringKeySet(namespace)) {
			if("".equals(ns) //Blank string; this means they want it to always match.
					|| key.matches(Pattern.quote(ns) + "(?:$|\\..*)")) {
				String[] split = key.split("\\.");
				list.add(split);
			}
		}
		return list;
	}

Replace Generic With Diamond HashSet<String[]> with HashSet<> in method public getNamespace(namespace String[]) : Set<String[]> from class com.laytonsmith.persistence.AbstractDataSource


	private static void init() {
		if(protocolHandlers == null) {
			protocolHandlers = new HashMap<>();
			Set<Class<?>> classes = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(datasource.class);
			for(Class<?> c : classes) {
				if(DataSource.class.isAssignableFrom(c)) {
					protocolHandlers.put((c.getAnnotation(datasource.class)).value(), c);
				} else {
					throw new Error(c.getName() + " does not implement DataSource!");
				}
			}
		}
	}

	private static void init() {
		if(protocolHandlers == null) {
			protocolHandlers = new HashMap<String, Class>();
			Set<Class<?>> classes = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(datasource.class);
			for(Class<?> c : classes) {
				if(DataSource.class.isAssignableFrom(c)) {
					protocolHandlers.put((c.getAnnotation(datasource.class)).value(), c);
				} else {
					throw new Error(c.getName() + " does not implement DataSource!");
				}
			}
		}
	}

Replace Generic With Diamond HashMap<String,Class> with HashMap<> in method private init() : void from class com.laytonsmith.persistence.DataSourceFactory


	/**
	 * Returns a list of supported protocols.
	 *
	 * @return
	 */
	public static Set<String> GetSupportedProtocols() {
		init();
		return new HashSet<>(protocolHandlers.keySet());
	}

	/**
	 * Returns a list of supported protocols.
	 *
	 * @return
	 */
	public static Set<String> GetSupportedProtocols() {
		init();
		return new HashSet<String>(protocolHandlers.keySet());
	}

Replace Generic With Diamond HashSet<String> with HashSet<> in method public GetSupportedProtocols() : Set<String> from class com.laytonsmith.persistence.DataSourceFactory


	@Override
	public Set<String[]> keySet(String[] keyBase) throws DataSourceException {
		connect();
		Set<String> ret;
		String kb = StringUtils.Join(keyBase, ".") + "*";
		try {
			if(inTransaction()) {
				ret = transaction.keys(kb).get();
			} else {
				ret = connection.keys(kb);
			}
			lastConnected = System.currentTimeMillis();
		} catch (JedisConnectionException e) {
			throw new DataSourceException(e);
		}
		Set<String[]> parsed = new HashSet<>();
		for(String s : ret) {
			parsed.add(s.split("\\."));
		}
		return parsed;
	}

	@Override
	public Set<String[]> keySet(String[] keyBase) throws DataSourceException {
		connect();
		Set<String> ret;
		String kb = StringUtils.Join(keyBase, ".") + "*";
		try {
			if(inTransaction()) {
				ret = transaction.keys(kb).get();
			} else {
				ret = connection.keys(kb);
			}
			lastConnected = System.currentTimeMillis();
		} catch (JedisConnectionException e) {
			throw new DataSourceException(e);
		}
		Set<String[]> parsed = new HashSet<String[]>();
		for(String s : ret) {
			parsed.add(s.split("\\."));
		}
		return parsed;
	}

Replace Generic With Diamond HashSet<String[]> with HashSet<> in method public keySet(keyBase String[]) : Set<String[]> from class com.laytonsmith.persistence.RedisDataSource


	/**
	 * Sets the server type in normal usage. In normal usage, this can only be called once, and additional calls
	 * are an error. While {@link #forceServerType(com.laytonsmith.abstraction.Implementation.Type)} does exist, and
	 * can be used to bypass this, and it may be tempting to check server if {@link #GetServerType} throws an exception
	 * before setting this, these temptations should be avoided (except where explicitely allowed) as fixing your code
	 * in any other way will inevitably lead to other problems down the road. This code should only be called once,
	 * with the correct type, since the code makes assumptions based on the first type sent to it. Additionally, setting
	 * the type twice with the same type, while it would not directly cause an error, is still a serious code smell, and
	 * indicates a larger code organization issue, and so temptation should still be avoided to modifying this code
	 * to allow the same server type to be set the second time, or causing the second call to just be ignored.
	 * @param type The server type to set.
	 */
	public static void setServerType(Implementation.Type type) {
		if(serverType == null) {
			serverType = type;
		} else {
			if(type != Type.TEST) { //This could potentially happen, but we don't care in the case that we
				//are testing, so don't error out here. (Failures may occur elsewhere though... :()
				throw new RuntimeException("Server type is already set! Cannot re-set!");
			}
		}

		//Fire off our abstractionenum checks in a new Thread
		if(type != Type.TEST && type != Type.SHELL && useAbstractEnumThread) {
			Thread abstractionenumsThread;
			abstractionenumsThread = new Thread(() -> {
				try {
					try {
						//Let the server startup data blindness go by first, so we display any error messages prominently,
						//since an Error is a case of very bad code that shouldn't have been released to begin with.
						Thread.sleep(15000);
					} catch (InterruptedException ex) {
						//
					}
					Set<Class<?>> abstractionenums = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(abstractionenum.class);
					for(Class c : abstractionenums) {
						abstractionenum annotation = (abstractionenum) c.getAnnotation(abstractionenum.class);
						if(EnumConvertor.class.isAssignableFrom(c)) {
							EnumConvertor<Enum, Enum> convertor;
							try {
								//Now, if this is not the current server type, skip it
								if(annotation.implementation() != serverType) {
									continue;
								}
								//Next, verify usage of the annotation (it is an error if not used properly)
								//All EnumConvertor subclasses should have public static getConvertor methods, let's grab it now
								Method m = c.getDeclaredMethod("getConvertor");
								convertor = (EnumConvertor<Enum, Enum>) m.invoke(null);
								//Go through and check for a proper mapping both ways, from concrete to abstract, and vice versa.
								//At this point, if there is an error, it is only a warning, NOT an error.
								Class abstractEnum = annotation.forAbstractEnum();
								Class concreteEnum = annotation.forConcreteEnum();
								checkEnumConvertors(convertor, abstractEnum, concreteEnum, false);
								checkEnumConvertors(convertor, concreteEnum, abstractEnum, true);

							} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
								throw new Error(ex);
							} catch (NoSuchMethodException ex) {
								throw new Error(serverType.getBranding() + ": The method with signature public static " + c.getName() + " getConvertor() was not found in " + c.getName()
										+ " Please add the following code: \n"
												+ "private static " + c.getName() + " instance;\n"
														+ "public static " + c.getName() + " getConvertor(){\n"
																+ "\tif(instance == null){\n"
																+ "\t\tinstance = new " + c.getName() + "();\n"
																		+ "\t}\n"
																		+ "\treturn instance;\n"
																		+ "}\n"
																		+ "If you do not know what  error is, please report this to the developers.");
							}
						} else {
							throw new Error("Only classes that extend EnumConvertor may use @abstractionenum. " + c.getName() + " does not, yet it uses the annotation.");
						}

					}
				} catch (Exception e) {
					boolean debugMode;
					try {
						debugMode = Prefs.DebugMode();
					} catch (RuntimeException ex) {
						//Set it to true if we fail to load prefs, which can happen
						//with a buggy front end.
						debugMode = true;
					}
					if(debugMode) {
						//If we're in debug mode, sure, go ahead and print the stack trace,
						//but otherwise we don't want to bother the user.
						e.printStackTrace();
					}
				}
			}, "Abstraction Enum Verification Thread");
			abstractionenumsThread.setPriority(Thread.MIN_PRIORITY);
			abstractionenumsThread.setDaemon(true);
			abstractionenumsThread.start();
		}
	}

	public static void setServerType(Implementation.Type type) {
		if(serverType == null) {
			serverType = type;
		} else {
			if(type != Type.TEST) { //This could potentially happen, but we don't care in the case that we
				//are testing, so don't error out here. (Failures may occur elsewhere though... :()
				throw new RuntimeException("Server type is already set! Cannot re-set!");
			}
		}

		//Fire off our abstractionenum checks in a new Thread
		if(type != Type.TEST && type != Type.SHELL && useAbstractEnumThread) {
			Thread abstractionenumsThread;
			abstractionenumsThread = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						try {
							//Let the server startup data blindness go by first, so we display any error messages prominently,
							//since an Error is a case of very bad code that shouldn't have been released to begin with.
							Thread.sleep(15000);
						} catch (InterruptedException ex) {
							//
						}
						Set<Class<?>> abstractionenums = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(abstractionenum.class);
						for(Class c : abstractionenums) {
							abstractionenum annotation = (abstractionenum) c.getAnnotation(abstractionenum.class);
							if(EnumConvertor.class.isAssignableFrom(c)) {
								EnumConvertor<Enum, Enum> convertor;
								try {
									//Now, if this is not the current server type, skip it
									if(annotation.implementation() != serverType) {
										continue;
									}
									//Next, verify usage of the annotation (it is an error if not used properly)
									//All EnumConvertor subclasses should have public static getConvertor methods, let's grab it now
									Method m = c.getDeclaredMethod("getConvertor");
									convertor = (EnumConvertor<Enum, Enum>) m.invoke(null);
									//Go through and check for a proper mapping both ways, from concrete to abstract, and vice versa.
									//At this point, if there is an error, it is only a warning, NOT an error.
									Class abstractEnum = annotation.forAbstractEnum();
									Class concreteEnum = annotation.forConcreteEnum();
									checkEnumConvertors(convertor, abstractEnum, concreteEnum, false);
									checkEnumConvertors(convertor, concreteEnum, abstractEnum, true);

								} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
									throw new Error(ex);
								} catch (NoSuchMethodException ex) {
									throw new Error(serverType.getBranding() + ": The method with signature public static " + c.getName() + " getConvertor() was not found in " + c.getName()
											+ " Please add the following code: \n"
											+ "private static " + c.getName() + " instance;\n"
											+ "public static " + c.getName() + " getConvertor(){\n"
											+ "\tif(instance == null){\n"
											+ "\t\tinstance = new " + c.getName() + "();\n"
											+ "\t}\n"
											+ "\treturn instance;\n"
											+ "}\n"
											+ "If you do not know what  error is, please report this to the developers.");
								}
							} else {
								throw new Error("Only classes that extend EnumConvertor may use @abstractionenum. " + c.getName() + " does not, yet it uses the annotation.");
							}

						}
					} catch (Exception e) {
						boolean debugMode;
						try {
							debugMode = Prefs.DebugMode();
						} catch (RuntimeException ex) {
							//Set it to true if we fail to load prefs, which can happen
							//with a buggy front end.
							debugMode = true;
						}
						if(debugMode) {
							//If we're in debug mode, sure, go ahead and print the stack trace,
							//but otherwise we don't want to bother the user.
							e.printStackTrace();
						}
					}
				}
			}, "Abstraction Enum Verification Thread");
			abstractionenumsThread.setPriority(Thread.MIN_PRIORITY);
			abstractionenumsThread.setDaemon(true);
			abstractionenumsThread.start();
		}
	}

Replace Anonymous With Lambda com.laytonsmith.abstraction.Implementation.setServerType.new Thread.new Runnable with () -> {trytryThread.sleep(15000); in method public setServerType(type Implementation.Type) : void from class com.laytonsmith.abstraction.Implementation


		private void updateArgument(Argument a) {
			if(a == null) {
				return;
			}
			List<Argument> toRemove = new ArrayList<>();
			for(Argument arg : arguments) {
				if(arg.modelEquals(a)) {
					toRemove.add(arg);
				}
			}
			for(Argument arg : toRemove) {
				arguments.remove(arg);
			}
			arguments.add(a);
		}

		private void updateArgument(Argument a) {
			if(a == null) {
				return;
			}
			List<Argument> toRemove = new ArrayList<Argument>();
			for(Argument arg : arguments) {
				if(arg.modelEquals(a)) {
					toRemove.add(arg);
				}
			}
			for(Argument arg : toRemove) {
				arguments.remove(arg);
			}
			arguments.add(a);
		}

Replace Generic With Diamond ArrayList<Argument> with ArrayList<> in method private updateArgument(a Argument) : void from class com.laytonsmith.PureUtilities.ArgumentParser.ArgumentParserResults


		private List<String> getStringListArgument(Argument arg) {
			if(arg == null) {
				return null;
			}
			if(arg.argType != Type.ARRAY_OF_STRINGS) {
				throw new ClassCastException("Argument type not set to " + Type.ARRAY_OF_STRINGS.name() + ". Cannot return a " + "string list" + ".");
			}
			return new ArrayList<>(arg.arrayVal);
		}

		private List<String> getStringListArgument(Argument arg) {
			if(arg == null) {
				return null;
			}
			if(arg.argType != Type.ARRAY_OF_STRINGS) {
				throw new ClassCastException("Argument type not set to " + Type.ARRAY_OF_STRINGS.name() + ". Cannot return a " + "string list" + ".");
			}
			return new ArrayList<String>(arg.arrayVal);
		}

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method private getStringListArgument(arg Argument) : List<String> from class com.laytonsmith.PureUtilities.ArgumentParser.ArgumentParserResults


		private List<Double> getNumberListArgument(Argument arg) {
			if(arg == null) {
				return null;
			}
			if(arg.argType != Type.ARRAY_OF_NUMBERS) {
				throw new ClassCastException("Argument type not set to " + Type.ARRAY_OF_NUMBERS.name() + ". Cannot return a " + "number list" + ".");
			}

			List<Double> list = new ArrayList<>();
			for(String s : arg.arrayVal) {
				list.add(Double.parseDouble(s));
			}
			return list;
		}

		private List<Double> getNumberListArgument(Argument arg) {
			if(arg == null) {
				return null;
			}
			if(arg.argType != Type.ARRAY_OF_NUMBERS) {
				throw new ClassCastException("Argument type not set to " + Type.ARRAY_OF_NUMBERS.name() + ". Cannot return a " + "number list" + ".");
			}

			List<Double> list = new ArrayList<Double>();
			for(String s : arg.arrayVal) {
				list.add(Double.parseDouble(s));
			}
			return list;
		}

Replace Generic With Diamond ArrayList<Double> with ArrayList<> in method private getNumberListArgument(arg Argument) : List<Double> from class com.laytonsmith.PureUtilities.ArgumentParser.ArgumentParserResults


	/**
	 * Returns a simple List of the arguments, parsed into a proper argument
	 * list. This will work essentially identically to how general shell
	 * arguments are parsed.
	 *
	 * @param args
	 * @return
	 */
	static List<String> lex(String args) {
		//First, we have to tokenize the strings. Since we can have quoted arguments, we can't simply split on spaces.
		List<String> arguments = new ArrayList<>();
		StringBuilder buf = new StringBuilder();
		boolean stateInSingleQuote = false;
		boolean stateInDoubleQuote = false;
		for(int i = 0; i < args.length(); i++) {
			Character c0 = args.charAt(i);
			Character c1 = i + 1 < args.length() ? args.charAt(i + 1) : null;

			if(c0 == '\\') {
				if(c1 == '\'' && stateInSingleQuote
						|| c1 == '"' && stateInDoubleQuote
						|| c1 == ' ' && !stateInDoubleQuote && !stateInSingleQuote
						|| c1 == '\\' && (stateInDoubleQuote || stateInSingleQuote)) {
					//We are escaping the next character. Add it to the buffer instead, and
					//skip ahead two
					buf.append(c1);
					i++;
					continue;
				}

			}

			if(c0 == ' ') {
				if(!stateInDoubleQuote && !stateInSingleQuote) {
					//argument split
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					continue;
				}
			}
			if(c0 == '\'' && !stateInDoubleQuote) {
				if(stateInSingleQuote) {
					stateInSingleQuote = false;
					arguments.add(buf.toString());
					buf = new StringBuilder();
				} else {
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					stateInSingleQuote = true;
				}
				continue;
			}
			if(c0 == '"' && !stateInSingleQuote) {
				if(stateInDoubleQuote) {
					stateInDoubleQuote = false;
					arguments.add(buf.toString());
					buf = new StringBuilder();
				} else {
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					stateInDoubleQuote = true;
				}
				continue;
			}
			buf.append(c0);
		}
		if(buf.length() != 0) {
			arguments.add(buf.toString());
		}
		return arguments;
	}

	/**
	 * Returns a simple List of the arguments, parsed into a proper argument
	 * list. This will work essentially identically to how general shell
	 * arguments are parsed.
	 *
	 * @param args
	 * @return
	 */
	static List<String> lex(String args) {
		//First, we have to tokenize the strings. Since we can have quoted arguments, we can't simply split on spaces.
		List<String> arguments = new ArrayList<String>();
		StringBuilder buf = new StringBuilder();
		boolean stateInSingleQuote = false;
		boolean stateInDoubleQuote = false;
		for(int i = 0; i < args.length(); i++) {
			Character c0 = args.charAt(i);
			Character c1 = i + 1 < args.length() ? args.charAt(i + 1) : null;

			if(c0 == '\\') {
				if(c1 == '\'' && stateInSingleQuote
						|| c1 == '"' && stateInDoubleQuote
						|| c1 == ' ' && !stateInDoubleQuote && !stateInSingleQuote
						|| c1 == '\\' && (stateInDoubleQuote || stateInSingleQuote)) {
					//We are escaping the next character. Add it to the buffer instead, and
					//skip ahead two
					buf.append(c1);
					i++;
					continue;
				}

			}

			if(c0 == ' ') {
				if(!stateInDoubleQuote && !stateInSingleQuote) {
					//argument split
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					continue;
				}
			}
			if(c0 == '\'' && !stateInDoubleQuote) {
				if(stateInSingleQuote) {
					stateInSingleQuote = false;
					arguments.add(buf.toString());
					buf = new StringBuilder();
				} else {
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					stateInSingleQuote = true;
				}
				continue;
			}
			if(c0 == '"' && !stateInSingleQuote) {
				if(stateInDoubleQuote) {
					stateInDoubleQuote = false;
					arguments.add(buf.toString());
					buf = new StringBuilder();
				} else {
					if(buf.length() != 0) {
						arguments.add(buf.toString());
						buf = new StringBuilder();
					}
					stateInDoubleQuote = true;
				}
				continue;
			}
			buf.append(c0);
		}
		if(buf.length() != 0) {
			arguments.add(buf.toString());
		}
		return arguments;
	}

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method package lex(args String) : List<String> from class com.laytonsmith.PureUtilities.ArgumentParser


	/**
	 * Returns a list of all the header names that are set in this request.
	 *
	 * @return
	 */
	public Set<String> getHeaderNames() {
		Set<String> set = new HashSet<>();
		for(HTTPHeader h : headers) {
			if(h.getHeader() != null) {
				set.add(h.getHeader());
			}
		}
		return set;
	}

	/**
	 * Returns a list of all the header names that are set in this request.
	 *
	 * @return
	 */
	public Set<String> getHeaderNames() {
		Set<String> set = new HashSet<String>();
		for(HTTPHeader h : headers) {
			if(h.getHeader() != null) {
				set.add(h.getHeader());
			}
		}
		return set;
	}

Replace Generic With Diamond HashSet<String> with HashSet<> in method public getHeaderNames() : Set<String> from class com.laytonsmith.PureUtilities.Web.HTTPResponse


	/**
	 * Returns all the headers for a given key. If there are no headers set for this key, an empty list is returned.
	 *
	 * @param key
	 * @return
	 */
	public List<String> getHeaders(String key) {
		List<String> list = new ArrayList<>();
		for(HTTPHeader header : headers) {
			if((header.getHeader() == null && key == null) || (header.getHeader() != null && header.getHeader().equalsIgnoreCase(key))) {
				list.add(header.getValue());
			}
		}
		return list;
	}

	/**
	 * Returns all the headers for a given key. If there are no headers set for this key, an empty list is returned.
	 *
	 * @param key
	 * @return
	 */
	public List<String> getHeaders(String key) {
		List<String> list = new ArrayList<String>();
		for(HTTPHeader header : headers) {
			if((header.getHeader() == null && key == null) || (header.getHeader() != null && header.getHeader().equalsIgnoreCase(key))) {
				list.add(header.getValue());
			}
		}
		return list;
	}

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method public getHeaders(key String) : List<String> from class com.laytonsmith.PureUtilities.Web.HTTPResponse


	/**
	 * Recurses down into the tree, attempting to optimize where possible. A few things have strong coupling, for
	 * information on these items, see the documentation included in the source.
	 *
	 * @param tree
	 * @return
	 */
	private static void optimize(ParseTree tree, Stack<List<Procedure>> procs, Set<ConfigCompileException> compilerErrors) {
		if(tree.isOptimized()) {
			return; //Don't need to re-run this
		}
//		if(tree.getData() instanceof CIdentifier) {
//			optimize(((CIdentifier) tree.getData()).contained(), procs);
//			return;
//		}
		if(!(tree.getData() instanceof CFunction)) {
			//There's no way to optimize something that's not a function
			return;
		}
		//If it is a proc definition, we need to go ahead and see if we can add it to the const proc stack
		if(tree.getData().val().equals("proc")) {
			procs.push(new ArrayList<>());
		}
		CFunction cFunction = (CFunction) tree.getData();
		Function func;
		try {
			func = (Function) FunctionList.getFunction(cFunction);
		} catch (ConfigCompileException e) {
			func = null;
		}
		if(func != null) {
			if(func.getClass().getAnnotation(nolinking.class) != null) {
				//It's an unlinking function, so we need to stop at this point
				return;
			}
		}
		if(cFunction instanceof CIdentifier) {
			//Add the child to the identifier
			ParseTree c = ((CIdentifier) cFunction).contained();
			tree.addChild(c);
			if(c.getData() instanceof Construct) {
				((Construct) c.getData()).setWasIdentifier(true);
			}
		}

		List<ParseTree> children = tree.getChildren();
		if(func instanceof Optimizable && ((Optimizable) func).optimizationOptions().contains(OptimizationOption.PRIORITY_OPTIMIZATION)) {
			// This is a priority optimization function, meaning it needs to be optimized before its children are.
			// This is required when optimization of the children could cause different internal behavior, for instance
			// if this function is expecting the precense of soem code element, but the child gets optimized out, this
			// would cause an error, even though the user did in fact provide code in that section.
			try {
				((Optimizable) func).optimizeDynamic(tree.getTarget(), children, tree.getFileOptions());
			} catch (ConfigCompileException ex) {
				// If an error occurs, we will skip the rest of this element
				compilerErrors.add(ex);
				return;
			} catch (ConfigRuntimeException ex) {
				compilerErrors.add(new ConfigCompileException(ex));
				return;
			}
		}
		//Loop through the children, and if any of them are functions that are terminal, truncate.
		//To explain this further, consider the following:
		//For the code: concat(die(), msg('')), this diagram shows the abstract syntax tree:
		//         (concat)
		//        /        \
		//       /          \
		//     (die)       (msg)
		//By looking at the code, we can tell that msg() will never be called, because die() will run first,
		//and since it is a "terminal" function, any code after it will NEVER run. However, consider a more complex condition:
		// if(@input){ die() msg('1') } else { msg('2') msg('3') }
		//              if(@input)
		//        [true]/         \[false]
		//             /           \
		//         (sconcat)     (sconcat)
		//           /   \         /    \
		//          /     \       /      \
		//       (die) (msg[1])(msg[2]) (msg[3])
		//In this case, only msg('1') is guaranteed not to run, msg('2') and msg('3') will still run in some cases.
		//So, we can optimize out msg('1') in this case, which would cause the tree to become much simpler, therefore a worthwile optimization:
		//              if(@input)
		//        [true]/        \[false]
		//             /          \
		//          (die)      (sconcat)
		//                      /    \
		//                     /      \
		//                 (msg[2]) (msg[3])
		//We do have to be careful though, because of functions like if, which actually work like this:
		//if(@var){ die() } else { msg('') }
		//                (if)
		//              /  |  \
		//             /   |   \
		//          @var (die) (msg)
		//We can't git rid of the msg() here, because it is actually in another branch.
		//For the time being, we will simply say that if a function uses execs, it
		//is a branch (branches always use execs, though using execs doesn't strictly
		//mean you are a branch type function).

		for(int i = 0; i < children.size(); i++) {
			ParseTree t = children.get(i);
			if(t.getData() instanceof CFunction) {
				if(t.getData().val().startsWith("_") || (func != null && func.useSpecialExec())) {
					continue;
				}
				Function f;
				try {
					f = (Function) FunctionList.getFunction(((CFunction) t.getData()));
				} catch (ConfigCompileException | ClassCastException ex) {
					continue;
				}
				Set<OptimizationOption> options = NO_OPTIMIZATIONS;
				if(f instanceof Optimizable) {
					options = ((Optimizable) f).optimizationOptions();
				}
				if(options.contains(OptimizationOption.TERMINAL)) {
					if(children.size() > i + 1) {
						//First, a compiler warning
						CHLog.GetLogger().Log(CHLog.Tags.COMPILER, LogLevel.WARNING, "Unreachable code. Consider removing this code.", children.get(i + 1).getTarget());
						//Now, truncate the children
						for(int j = children.size() - 1; j > i; j--) {
							children.remove(j);
						}
						break;
					}
				}
			}
		}
		boolean fullyStatic = true;
		boolean hasIVars = false;
		for(ParseTree node : children) {
			if(node.getData() instanceof CFunction) {
				optimize(node, procs, compilerErrors);
			}

			if(node.getData() instanceof Construct) {
				Construct d = (Construct) node.getData();
				if(d.isDynamic() || (d instanceof IVariable)) {
					fullyStatic = false;
				}
			}
			if(node.getData() instanceof IVariable) {
				hasIVars = true;
			}
		}

		//In all cases, at this point, we are either unable to optimize, or we will
		//optimize, so set our optimized variable at this point.
		tree.setOptimized(true);

		if(func == null) {
			//It's a proc call. Let's see if we can optimize it
			Procedure p = null;
			loop:
			for(List<Procedure> proc : procs) {
				for(Procedure pp : proc) {
					if(pp.getName().equals(cFunction.val())) {
						p = pp;
						break loop;
					}
				}
			}
			if(p != null) {
				try {
					Mixed c = DataHandling.proc.optimizeProcedure(p.getTarget(), p, children);
					if(c != null) {
						tree.setData(c);
						tree.removeChildren();
						return;
					} //else Nope, couldn't optimize.
				} catch (ConfigRuntimeException ex) {
					//Cool. Caught a runtime error at compile time :D
					compilerErrors.add(new ConfigCompileException(ex));
				}
			}
			//else this procedure isn't listed yet. Maybe a compiler error, maybe not, depends,
			//so we can't for sure say, but we do know we can't optimize this
			return;
		}
		if(tree.getData().val().equals("proc")) {
			//Check for too few arguments
			if(children.size() < 2) {
				compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to proc",
						tree.getData().getTarget()));
				return;
			}
			//We just went out of scope, so we need to pop the layer of Procedures that
			//are internal to us
			procs.pop();
			//However, as a special function, we *might* be able to get a const proc out of this
			//Let's see.
			try {
				ParseTree root = new ParseTree(new CFunction(__autoconcat__, Target.UNKNOWN), tree.getFileOptions());
				Script fakeScript = Script.GenerateScript(root, "*");
				Environment env = null;
				try {
					if(Implementation.GetServerType().equals(Implementation.Type.BUKKIT)) {
						CommandHelperPlugin plugin = CommandHelperPlugin.self;
						GlobalEnv gEnv = new GlobalEnv(plugin.executionQueue, plugin.profiler, plugin.persistenceNetwork,
								MethodScriptFileLocations.getDefault().getConfigDirectory(), plugin.profiles, new TaskManager());
						env = Environment.createEnvironment(gEnv, new CommandHelperEnvironment());
					} else {
						env = Static.GenerateStandaloneEnvironment(false);
					}
				} catch (IOException | DataSourceException | URISyntaxException | Profiles.InvalidProfileException e) {
					//
				}
				Procedure myProc = DataHandling.proc.getProcedure(tree.getTarget(), env, fakeScript, children.toArray(new ParseTree[children.size()]));
				procs.peek().add(myProc); //Yep. So, we can move on with our lives now, and if it's used later, it could possibly be static.
			} catch (ConfigRuntimeException e) {
				//Well, they have an error in there somewhere
				compilerErrors.add(new ConfigCompileException(e));
			} catch (NullPointerException e) {
				//Nope, can't optimize.
				return;
			}
		}

		//the compiler trick functions know how to deal with it specially, even if everything isn't
		//static, so do this first.
		String oldFunctionName = func.getName();
		Set<OptimizationOption> options = NO_OPTIMIZATIONS;
		if(func instanceof Optimizable) {
			options = ((Optimizable) func).optimizationOptions();
		}
		if(options.contains(OptimizationOption.OPTIMIZE_DYNAMIC)) {
			try {
				ParseTree tempNode;
				try {
					tempNode = ((Optimizable) func).optimizeDynamic(tree.getData().getTarget(), tree.getChildren(), tree.getFileOptions());
				} catch (ConfigRuntimeException e) {
					//Turn it into a compile exception, then rethrow
					throw new ConfigCompileException(e);
				}
				if(tempNode == Optimizable.PULL_ME_UP) {
					if(tree.hasChildren()) {
						tempNode = tree.getChildAt(0);
					} else {
						tempNode = null;
					}
				}
				if(tempNode == Optimizable.REMOVE_ME) {
					tree.setData(new CFunction("p", Target.UNKNOWN));
					tree.removeChildren();
				} else if(tempNode != null) {
					tree.setData(tempNode.getData());
					tree.setOptimized(tempNode.isOptimized());
					tree.setChildren(tempNode.getChildren());
					Construct.SetWasIdentifierHelper(tree.getData(), tempNode.getData(), false);
					optimize(tree, procs, compilerErrors);
					tree.setOptimized(true);
					//Some functions can actually make static the arguments, for instance, by pulling up a hardcoded
					//array, so if they have reversed this, make note of that now
					if(tempNode.hasBeenMadeStatic()) {
						fullyStatic = true;
					}
				} //else it wasn't an optimization, but a compile check
			} catch (ConfigCompileException ex) {
				compilerErrors.add(ex);
			}
		}
		if(!fullyStatic) {
			return;
		}
		//Otherwise, everything is static, or an IVariable and we can proceed.
		//Note since we could still have IVariables, we have to handle those
		//specially from here forward
		if(func.preResolveVariables() && hasIVars) {
			//Well, this function isn't equipped to deal with IVariables.
			return;
		}
		//It could have optimized by changing the name, in that case, we
		//don't want to run this now
		if(tree.getData().val().equals(oldFunctionName)
				&& (options.contains(OptimizationOption.OPTIMIZE_CONSTANT) || options.contains(OptimizationOption.CONSTANT_OFFLINE))) {
			Mixed[] constructs = new Mixed[tree.getChildren().size()];
			for(int i = 0; i < tree.getChildren().size(); i++) {
				constructs[i] = tree.getChildAt(i).getData();
			}
			try {
				try {
					Mixed result;
					if(options.contains(OptimizationOption.CONSTANT_OFFLINE)) {
						List<Integer> numArgsList = Arrays.asList(func.numArgs());
						if(!numArgsList.contains(Integer.MAX_VALUE)
								&& !numArgsList.contains(tree.getChildren().size())) {
							compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to "
									+ tree.getData().val(), tree.getData().getTarget()));
							result = null;
						} else {
							result = func.exec(tree.getData().getTarget(), null, constructs);
						}
					} else {
						result = ((Optimizable) func).optimize(tree.getData().getTarget(), constructs);
					}

					//If the result is null, it was just a check, it can't optimize further.
					if(result != null) {
						Construct.SetWasIdentifierHelper(tree.getData(), result, false);
						tree.setData(result);
						tree.removeChildren();
					}
				} catch (ConfigRuntimeException e) {
					//Turn this into a ConfigCompileException, then rethrow
					throw new ConfigCompileException(e);
				}
			} catch (ConfigCompileException ex) {
				compilerErrors.add(ex);
			}
		}

		//It doesn't know how to optimize. Oh well.
	}

	/**
	 * Recurses down into the tree, attempting to optimize where possible. A few things have strong coupling, for
	 * information on these items, see the documentation included in the source.
	 *
	 * @param tree
	 * @return
	 */
	private static void optimize(ParseTree tree, Stack<List<Procedure>> procs, Set<ConfigCompileException> compilerErrors) {
		if(tree.isOptimized()) {
			return; //Don't need to re-run this
		}
//		if(tree.getData() instanceof CIdentifier) {
//			optimize(((CIdentifier) tree.getData()).contained(), procs);
//			return;
//		}
		if(!(tree.getData() instanceof CFunction)) {
			//There's no way to optimize something that's not a function
			return;
		}
		//If it is a proc definition, we need to go ahead and see if we can add it to the const proc stack
		if(tree.getData().val().equals("proc")) {
			procs.push(new ArrayList<Procedure>());
		}
		CFunction cFunction = (CFunction) tree.getData();
		Function func;
		try {
			func = (Function) FunctionList.getFunction(cFunction);
		} catch (ConfigCompileException e) {
			func = null;
		}
		if(func != null) {
			if(func.getClass().getAnnotation(nolinking.class) != null) {
				//It's an unlinking function, so we need to stop at this point
				return;
			}
		}
		if(cFunction instanceof CIdentifier) {
			//Add the child to the identifier
			ParseTree c = ((CIdentifier) cFunction).contained();
			tree.addChild(c);
			if(c.getData() instanceof Construct) {
				((Construct) c.getData()).setWasIdentifier(true);
			}
		}

		List<ParseTree> children = tree.getChildren();
		if(func instanceof Optimizable && ((Optimizable) func).optimizationOptions().contains(OptimizationOption.PRIORITY_OPTIMIZATION)) {
			// This is a priority optimization function, meaning it needs to be optimized before its children are.
			// This is required when optimization of the children could cause different internal behavior, for instance
			// if this function is expecting the precense of soem code element, but the child gets optimized out, this
			// would cause an error, even though the user did in fact provide code in that section.
			try {
				((Optimizable) func).optimizeDynamic(tree.getTarget(), children, tree.getFileOptions());
			} catch (ConfigCompileException ex) {
				// If an error occurs, we will skip the rest of this element
				compilerErrors.add(ex);
				return;
			} catch (ConfigRuntimeException ex) {
				compilerErrors.add(new ConfigCompileException(ex));
				return;
			}
		}
		//Loop through the children, and if any of them are functions that are terminal, truncate.
		//To explain this further, consider the following:
		//For the code: concat(die(), msg('')), this diagram shows the abstract syntax tree:
		//         (concat)
		//        /        \
		//       /          \
		//     (die)       (msg)
		//By looking at the code, we can tell that msg() will never be called, because die() will run first,
		//and since it is a "terminal" function, any code after it will NEVER run. However, consider a more complex condition:
		// if(@input){ die() msg('1') } else { msg('2') msg('3') }
		//              if(@input)
		//        [true]/         \[false]
		//             /           \
		//         (sconcat)     (sconcat)
		//           /   \         /    \
		//          /     \       /      \
		//       (die) (msg[1])(msg[2]) (msg[3])
		//In this case, only msg('1') is guaranteed not to run, msg('2') and msg('3') will still run in some cases.
		//So, we can optimize out msg('1') in this case, which would cause the tree to become much simpler, therefore a worthwile optimization:
		//              if(@input)
		//        [true]/        \[false]
		//             /          \
		//          (die)      (sconcat)
		//                      /    \
		//                     /      \
		//                 (msg[2]) (msg[3])
		//We do have to be careful though, because of functions like if, which actually work like this:
		//if(@var){ die() } else { msg('') }
		//                (if)
		//              /  |  \
		//             /   |   \
		//          @var (die) (msg)
		//We can't git rid of the msg() here, because it is actually in another branch.
		//For the time being, we will simply say that if a function uses execs, it
		//is a branch (branches always use execs, though using execs doesn't strictly
		//mean you are a branch type function).

		for(int i = 0; i < children.size(); i++) {
			ParseTree t = children.get(i);
			if(t.getData() instanceof CFunction) {
				if(t.getData().val().startsWith("_") || (func != null && func.useSpecialExec())) {
					continue;
				}
				Function f;
				try {
					f = (Function) FunctionList.getFunction(((CFunction) t.getData()));
				} catch (ConfigCompileException | ClassCastException ex) {
					continue;
				}
				Set<OptimizationOption> options = NO_OPTIMIZATIONS;
				if(f instanceof Optimizable) {
					options = ((Optimizable) f).optimizationOptions();
				}
				if(options.contains(OptimizationOption.TERMINAL)) {
					if(children.size() > i + 1) {
						//First, a compiler warning
						CHLog.GetLogger().Log(CHLog.Tags.COMPILER, LogLevel.WARNING, "Unreachable code. Consider removing this code.", children.get(i + 1).getTarget());
						//Now, truncate the children
						for(int j = children.size() - 1; j > i; j--) {
							children.remove(j);
						}
						break;
					}
				}
			}
		}
		boolean fullyStatic = true;
		boolean hasIVars = false;
		for(ParseTree node : children) {
			if(node.getData() instanceof CFunction) {
				optimize(node, procs, compilerErrors);
			}

			if(node.getData() instanceof Construct) {
				Construct d = (Construct) node.getData();
				if(d.isDynamic() || (d instanceof IVariable)) {
					fullyStatic = false;
				}
			}
			if(node.getData() instanceof IVariable) {
				hasIVars = true;
			}
		}

		//In all cases, at this point, we are either unable to optimize, or we will
		//optimize, so set our optimized variable at this point.
		tree.setOptimized(true);

		if(func == null) {
			//It's a proc call. Let's see if we can optimize it
			Procedure p = null;
			loop:
			for(List<Procedure> proc : procs) {
				for(Procedure pp : proc) {
					if(pp.getName().equals(cFunction.val())) {
						p = pp;
						break loop;
					}
				}
			}
			if(p != null) {
				try {
					Mixed c = DataHandling.proc.optimizeProcedure(p.getTarget(), p, children);
					if(c != null) {
						tree.setData(c);
						tree.removeChildren();
						return;
					} //else Nope, couldn't optimize.
				} catch (ConfigRuntimeException ex) {
					//Cool. Caught a runtime error at compile time :D
					compilerErrors.add(new ConfigCompileException(ex));
				}
			}
			//else this procedure isn't listed yet. Maybe a compiler error, maybe not, depends,
			//so we can't for sure say, but we do know we can't optimize this
			return;
		}
		if(tree.getData().val().equals("proc")) {
			//Check for too few arguments
			if(children.size() < 2) {
				compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to proc",
						tree.getData().getTarget()));
				return;
			}
			//We just went out of scope, so we need to pop the layer of Procedures that
			//are internal to us
			procs.pop();
			//However, as a special function, we *might* be able to get a const proc out of this
			//Let's see.
			try {
				ParseTree root = new ParseTree(new CFunction(__autoconcat__, Target.UNKNOWN), tree.getFileOptions());
				Script fakeScript = Script.GenerateScript(root, "*");
				Environment env = null;
				try {
					if(Implementation.GetServerType().equals(Implementation.Type.BUKKIT)) {
						CommandHelperPlugin plugin = CommandHelperPlugin.self;
						GlobalEnv gEnv = new GlobalEnv(plugin.executionQueue, plugin.profiler, plugin.persistenceNetwork,
								MethodScriptFileLocations.getDefault().getConfigDirectory(), plugin.profiles, new TaskManager());
						env = Environment.createEnvironment(gEnv, new CommandHelperEnvironment());
					} else {
						env = Static.GenerateStandaloneEnvironment(false);
					}
				} catch (IOException | DataSourceException | URISyntaxException | Profiles.InvalidProfileException e) {
					//
				}
				Procedure myProc = DataHandling.proc.getProcedure(tree.getTarget(), env, fakeScript, children.toArray(new ParseTree[children.size()]));
				procs.peek().add(myProc); //Yep. So, we can move on with our lives now, and if it's used later, it could possibly be static.
			} catch (ConfigRuntimeException e) {
				//Well, they have an error in there somewhere
				compilerErrors.add(new ConfigCompileException(e));
			} catch (NullPointerException e) {
				//Nope, can't optimize.
				return;
			}
		}

		//the compiler trick functions know how to deal with it specially, even if everything isn't
		//static, so do this first.
		String oldFunctionName = func.getName();
		Set<OptimizationOption> options = NO_OPTIMIZATIONS;
		if(func instanceof Optimizable) {
			options = ((Optimizable) func).optimizationOptions();
		}
		if(options.contains(OptimizationOption.OPTIMIZE_DYNAMIC)) {
			try {
				ParseTree tempNode;
				try {
					tempNode = ((Optimizable) func).optimizeDynamic(tree.getData().getTarget(), tree.getChildren(), tree.getFileOptions());
				} catch (ConfigRuntimeException e) {
					//Turn it into a compile exception, then rethrow
					throw new ConfigCompileException(e);
				}
				if(tempNode == Optimizable.PULL_ME_UP) {
					if(tree.hasChildren()) {
						tempNode = tree.getChildAt(0);
					} else {
						tempNode = null;
					}
				}
				if(tempNode == Optimizable.REMOVE_ME) {
					tree.setData(new CFunction("p", Target.UNKNOWN));
					tree.removeChildren();
				} else if(tempNode != null) {
					tree.setData(tempNode.getData());
					tree.setOptimized(tempNode.isOptimized());
					tree.setChildren(tempNode.getChildren());
					Construct.SetWasIdentifierHelper(tree.getData(), tempNode.getData(), false);
					optimize(tree, procs, compilerErrors);
					tree.setOptimized(true);
					//Some functions can actually make static the arguments, for instance, by pulling up a hardcoded
					//array, so if they have reversed this, make note of that now
					if(tempNode.hasBeenMadeStatic()) {
						fullyStatic = true;
					}
				} //else it wasn't an optimization, but a compile check
			} catch (ConfigCompileException ex) {
				compilerErrors.add(ex);
			}
		}
		if(!fullyStatic) {
			return;
		}
		//Otherwise, everything is static, or an IVariable and we can proceed.
		//Note since we could still have IVariables, we have to handle those
		//specially from here forward
		if(func.preResolveVariables() && hasIVars) {
			//Well, this function isn't equipped to deal with IVariables.
			return;
		}
		//It could have optimized by changing the name, in that case, we
		//don't want to run this now
		if(tree.getData().val().equals(oldFunctionName)
				&& (options.contains(OptimizationOption.OPTIMIZE_CONSTANT) || options.contains(OptimizationOption.CONSTANT_OFFLINE))) {
			Mixed[] constructs = new Mixed[tree.getChildren().size()];
			for(int i = 0; i < tree.getChildren().size(); i++) {
				constructs[i] = tree.getChildAt(i).getData();
			}
			try {
				try {
					Mixed result;
					if(options.contains(OptimizationOption.CONSTANT_OFFLINE)) {
						List<Integer> numArgsList = Arrays.asList(func.numArgs());
						if(!numArgsList.contains(Integer.MAX_VALUE)
								&& !numArgsList.contains(tree.getChildren().size())) {
							compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to "
									+ tree.getData().val(), tree.getData().getTarget()));
							result = null;
						} else {
							result = func.exec(tree.getData().getTarget(), null, constructs);
						}
					} else {
						result = ((Optimizable) func).optimize(tree.getData().getTarget(), constructs);
					}

					//If the result is null, it was just a check, it can't optimize further.
					if(result != null) {
						Construct.SetWasIdentifierHelper(tree.getData(), result, false);
						tree.setData(result);
						tree.removeChildren();
					}
				} catch (ConfigRuntimeException e) {
					//Turn this into a ConfigCompileException, then rethrow
					throw new ConfigCompileException(e);
				}
			} catch (ConfigCompileException ex) {
				compilerErrors.add(ex);
			}
		}

		//It doesn't know how to optimize. Oh well.
	}

Replace Generic With Diamond ArrayList<Procedure> with ArrayList<> in method private optimize(tree ParseTree, procs Stack<List<Procedure>>, compilerErrors Set<ConfigCompileException>) : void from class com.laytonsmith.core.MethodScriptCompiler


	private static Preferences GetPrefs(File initFile) throws IOException {
		List<Preference> defaults = new ArrayList<>(Arrays.asList(new Preference[]{
			new Preference("profiler-on", "false", Preferences.Type.BOOLEAN, "Turns the profiler on or off. The profiler can cause a slight amount of lag, so generally speaking"
			+ " you don't leave it on during normal operation."),
			new Preference("profiler-granularity", "1", Preferences.Type.INT, "Sets the granularity of the profiler. 1 logs some things, while 5 logs everything possible."),
			new Preference("profiler-log", "logs/profiling/internal/%Y-%M-%D-profiler.log", Preferences.Type.STRING, "The location of the profiler output log. The following macros are supported"
			+ " and will expand to the specified values: %Y - Year, %M - Month, %D - Day, %h - Hour, %m - Minute, %s - Second"),
			new Preference("write-to-file", "true", Preferences.Type.BOOLEAN, "If true, will write results out to file."),
			new Preference("write-to-screen", "false", Preferences.Type.BOOLEAN, "If true, will write results out to screen."),
			new Preference("profile-log-threshold", "0.005", Preferences.Type.DOUBLE, "If a profile point took less than this amount of time (in ms) to run, it won't be logged. This is good for reducing data blindness"
			+ " caused by too much data being displayed. Normally you only care about things that took longer than a certain amount, not things that took less than a certain amount. Setting this to 0"
			+ " will trigger everything.")}));
		Preferences prefs = new Preferences("CommandHelper", Static.getLogger(), defaults, "These settings control the integrated profiler");
		prefs.init(initFile);
		return prefs;
	}

	private static Preferences GetPrefs(File initFile) throws IOException {
		List<Preference> defaults = new ArrayList<Preference>(Arrays.asList(new Preference[]{
			new Preference("profiler-on", "false", Preferences.Type.BOOLEAN, "Turns the profiler on or off. The profiler can cause a slight amount of lag, so generally speaking"
			+ " you don't leave it on during normal operation."),
			new Preference("profiler-granularity", "1", Preferences.Type.INT, "Sets the granularity of the profiler. 1 logs some things, while 5 logs everything possible."),
			new Preference("profiler-log", "logs/profiling/internal/%Y-%M-%D-profiler.log", Preferences.Type.STRING, "The location of the profiler output log. The following macros are supported"
			+ " and will expand to the specified values: %Y - Year, %M - Month, %D - Day, %h - Hour, %m - Minute, %s - Second"),
			new Preference("write-to-file", "true", Preferences.Type.BOOLEAN, "If true, will write results out to file."),
			new Preference("write-to-screen", "false", Preferences.Type.BOOLEAN, "If true, will write results out to screen."),
			new Preference("profile-log-threshold", "0.005", Preferences.Type.DOUBLE, "If a profile point took less than this amount of time (in ms) to run, it won't be logged. This is good for reducing data blindness"
			+ " caused by too much data being displayed. Normally you only care about things that took longer than a certain amount, not things that took less than a certain amount. Setting this to 0"
			+ " will trigger everything.")}));
		Preferences prefs = new Preferences("CommandHelper", Static.getLogger(), defaults, "These settings control the integrated profiler");
		prefs.init(initFile);
		return prefs;
	}

Replace Generic With Diamond ArrayList<Preference> with ArrayList<> in method private GetPrefs(initFile File) : Preferences from class com.laytonsmith.core.profiler.Profiler


	/**
	 * Pushes a log to either the screen or the log file, depending on config settings. Arbitrary messages can be logged
	 * using this method.
	 *
	 * @param message
	 */
	public void doLog(final String message) {
		outputQueue.push(null, null, () -> {
			if(writeToScreen) {
				StreamUtils.GetSystemOut().println(message);
			}
			if(writeToFile) {
				File file = new File(initFile.getParentFile(), DateUtils.ParseCalendarNotation(logFile));
				try {
					FileUtil.write(DateUtils.ParseCalendarNotation("%Y-%M-%D %h:%m.%s") + ": " + message + Static.LF(), //Message to log
							file, //File to output to
							FileUtil.APPEND, //We want to append
							true); //Create it for us if it doesn't exist
				} catch (IOException ex) {
					StreamUtils.GetSystemErr().println("While trying to write to the profiler log file (" + file.getAbsolutePath() + "), recieved an IOException: " + ex.getMessage());
				}
			}
		});
	}

	/**
	 * Pushes a log to either the screen or the log file, depending on config settings. Arbitrary messages can be logged
	 * using this method.
	 *
	 * @param message
	 */
	public void doLog(final String message) {
		outputQueue.push(null, null, new Runnable() {
			@Override
			public void run() {
				if(writeToScreen) {
					StreamUtils.GetSystemOut().println(message);
				}
				if(writeToFile) {
					File file = new File(initFile.getParentFile(), DateUtils.ParseCalendarNotation(logFile));
					try {
						FileUtil.write(DateUtils.ParseCalendarNotation("%Y-%M-%D %h:%m.%s") + ": " + message + Static.LF(), //Message to log
								file, //File to output to
								FileUtil.APPEND, //We want to append
								true); //Create it for us if it doesn't exist
					} catch (IOException ex) {
						StreamUtils.GetSystemErr().println("While trying to write to the profiler log file (" + file.getAbsolutePath() + "), recieved an IOException: " + ex.getMessage());
					}
				}

			}
		});
	}

Replace Anonymous With Lambda com.laytonsmith.core.profiler.Profiler.doLog.push.new Runnable with () -> {if(writeToScreen){StreamUtils.GetSystemOut().println(message); in method public doLog(message String) : void from class com.laytonsmith.core.profiler.Profiler


	/**
	 * If it is ok to by default do a simple conversion from a CArray to a Map, this method can do it for you. Likely
	 * this is not acceptable, so hard-coding the conversion will be necessary.
	 *
	 * @param manualObject
	 * @return
	 */
	public static Object DoConvert(CArray manualObject) {
		Map<String, Mixed> map = new HashMap<>();
		for(String key : manualObject.stringKeySet()) {
			map.put(key, manualObject.get(key, Target.UNKNOWN));
		}
		return map;
	}

	/**
	 * If it is ok to by default do a simple conversion from a CArray to a Map, this method can do it for you. Likely
	 * this is not acceptable, so hard-coding the conversion will be necessary.
	 *
	 * @param manualObject
	 * @return
	 */
	public static Object DoConvert(CArray manualObject) {
		Map<String, Construct> map = new HashMap<String, Construct>();
		for(String key : manualObject.stringKeySet()) {
			map.put(key, manualObject.get(key, Target.UNKNOWN));
		}
		return map;
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public DoConvert(manualObject CArray) : Object from class com.laytonsmith.core.events.AbstractEvent


	/**
	 * Test of match method, of class Prefilters.
	 */
	@Test
	public void testRegexMatch() {
		Map<String, Mixed> map = new HashMap<>();
		map.put("x", C.String("/1|2|3/"));
		try {
			Prefilters.match(map, "x", C.Int(2), PrefilterType.REGEX);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", C.Int(4), PrefilterType.REGEX);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

	/**
	 * Test of match method, of class Prefilters.
	 */
	@Test
	public void testRegexMatch() {
		Map<String, Construct> map = new HashMap<String, Construct>();
		map.put("x", C.String("/1|2|3/"));
		try {
			Prefilters.match(map, "x", C.Int(2), PrefilterType.REGEX);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", C.Int(4), PrefilterType.REGEX);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public testRegexMatch() : void from class com.laytonsmith.core.events.PrefiltersTest


	@Test
	public void testItemMatch() {
		Map<String, Mixed> map = new HashMap<>();
		map.put("x", C.String("35:2"));
		try {
			Prefilters.match(map, "x", "35:4", PrefilterType.ITEM_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "35", PrefilterType.ITEM_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "36:2", PrefilterType.ITEM_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}

	}

	@Test
	public void testItemMatch() {
		Map<String, Construct> map = new HashMap<String, Construct>();
		map.put("x", C.String("35:2"));
		try {
			Prefilters.match(map, "x", "35:4", PrefilterType.ITEM_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "35", PrefilterType.ITEM_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "36:2", PrefilterType.ITEM_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}

	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public testItemMatch() : void from class com.laytonsmith.core.events.PrefiltersTest


	@Test
	public void testStringMatch() {
		Map<String, Mixed> map = new HashMap<>();
		map.put("x", C.String("test"));
		try {
			Prefilters.match(map, "x", "test", PrefilterType.STRING_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "nope", PrefilterType.STRING_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

	@Test
	public void testStringMatch() {
		Map<String, Construct> map = new HashMap<String, Construct>();
		map.put("x", C.String("test"));
		try {
			Prefilters.match(map, "x", "test", PrefilterType.STRING_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "nope", PrefilterType.STRING_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public testStringMatch() : void from class com.laytonsmith.core.events.PrefiltersTest


	@Test
	public void testMathMatch() {
		Map<String, Mixed> map = new HashMap<>();
		map.put("x", C.String("2"));
		try {
			Prefilters.match(map, "x", "2.0", PrefilterType.MATH_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "2.00001", PrefilterType.MATH_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

	@Test
	public void testMathMatch() {
		Map<String, Construct> map = new HashMap<String, Construct>();
		map.put("x", C.String("2"));
		try {
			Prefilters.match(map, "x", "2.0", PrefilterType.MATH_MATCH);
		} catch (PrefilterNonMatchException e) {
			fail("Expected a match here");
		}
		try {
			Prefilters.match(map, "x", "2.00001", PrefilterType.MATH_MATCH);
			fail("Did not expect a match here");
		} catch (PrefilterNonMatchException e) {
		}
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public testMathMatch() : void from class com.laytonsmith.core.events.PrefiltersTest


	@Test
	public void testExpressionMatch() {
		Map<String, Mixed> map = new HashMap<>();
		map.put("x", C.String("(x > 4)"));
		try {
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}

			map.put("x", C.String("(x == 5)"));
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}

			map.put("x", C.String("(2 + 3)"));
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}
		} catch (ConfigRuntimeException e) {
			if(e.getCause() instanceof ClassNotFoundException) {
				// Nothing we can do during testing
			} else {
				fail("Test may be incorrectly formatted: " + e.getCause().getMessage());
			}
		}
	}

	@Test
	public void testExpressionMatch() {
		Map<String, Construct> map = new HashMap<String, Construct>();
		map.put("x", C.String("(x > 4)"));
		try {
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}

			map.put("x", C.String("(x == 5)"));
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}

			map.put("x", C.String("(2 + 3)"));
			try {
				Prefilters.match(map, "x", "5", PrefilterType.EXPRESSION);
			} catch (PrefilterNonMatchException e) {
				fail("Expected a match here");
			}
			try {
				Prefilters.match(map, "x", "4", PrefilterType.EXPRESSION);
				fail("Did not expect a match here");
			} catch (PrefilterNonMatchException e) {
			}
		} catch (ConfigRuntimeException e) {
			if(e.getCause() instanceof ClassNotFoundException) {
				// Nothing we can do during testing
			} else {
				fail("Test may be incorrectly formatted: " + e.getCause().getMessage());
			}
		}
	}

Replace Generic With Diamond HashMap<String,Construct> with HashMap<> in method public testExpressionMatch() : void from class com.laytonsmith.core.events.PrefiltersTest


	/**
	 * Returns the most likely character encoding for this file. The default is "ASCII" and is probably the most common.
	 * Note that ASCII and UTF-8 are the same format when the character set is just the 8 byte characters.
	 *
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static String getFileCharset(File file) throws IOException {
		int lang = nsPSMDetector.ALL;
		nsDetector det = new nsDetector(lang);
		final MutableObject result = new MutableObject("ASCII");
		det.Init((String charset) -> {
			result.setObject(charset);
		});

		BufferedInputStream imp = null;
		try {
			imp = new BufferedInputStream(new FileInputStream(file));
			byte[] buf = new byte[1024];
			int len;
			boolean done = false;
			boolean isAscii = true;

			while((len = imp.read(buf, 0, buf.length)) != -1) {
				if(isAscii) {
					isAscii = det.isAscii(buf, len);
				}
				if(!isAscii && !done) {
					done = det.DoIt(buf, len, false);
				}
			}
			det.DataEnd();
			return (String) result.getObject();
		} finally {
			if(imp != null) {
				imp.close();
			}
		}
	}

	/**
	 * Returns the most likely character encoding for this file. The default is "ASCII" and is probably the most common.
	 *
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static String getFileCharset(File file) throws IOException {
		int lang = nsPSMDetector.ALL;
		nsDetector det = new nsDetector(lang);
		final MutableObject result = new MutableObject("ASCII");
		det.Init(new nsICharsetDetectionObserver() {

			@Override
			public void Notify(String charset) {
				result.setObject(charset);
			}
		});

		BufferedInputStream imp = null;
		try {
			imp = new BufferedInputStream(new FileInputStream(file));
			byte[] buf = new byte[1024];
			int len;
			boolean done = false;
			boolean isAscii = true;

			while((len = imp.read(buf, 0, buf.length)) != -1) {
				if(isAscii) {
					isAscii = det.isAscii(buf, len);
				}
				if(!isAscii && !done) {
					done = det.DoIt(buf, len, false);
				}
			}
			det.DataEnd();
			return (String) result.getObject();
		} finally {
			if(imp != null) {
				imp.close();
			}
		}
	}

Replace Anonymous With Lambda com.laytonsmith.PureUtilities.Common.FileUtil.getFileCharset.Init.new nsICharsetDetectionObserver with (charset) -> {result.setObject(charset); in method public getFileCharset(file File) : String from class com.laytonsmith.PureUtilities.Common.FileUtil


		@Override
		public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
			MCServer server = StaticLayer.GetServer();
			int index = -1;
			if(args.length == 0) {
				index = -1;
			} else if(args.length == 1) {
				index = Static.getInt32(args[0], t);
			}

			if(index < -1 || index > 16) {
				throw new CRERangeException(this.getName() + " expects the index to be between -1 and 16 (inclusive)", t);
			}

			ArrayList<Construct> retVals = new ArrayList<>();

			if(index == 0 || index == -1) {
				//Server name
				retVals.add(new CString(server.getServerName(), t));
			}

			if(index == 1 || index == -1) {
				// API Version
				retVals.add(new CString(server.getAPIVersion(), t));
			}
			if(index == 2 || index == -1) {
				// Server Version
				retVals.add(new CString(server.getServerVersion(), t));
			}
			if(index == 3 || index == -1) {
				//Allow flight
				retVals.add(CBoolean.get(server.getAllowFlight()));
			}
			if(index == 4 || index == -1) {
				//Allow nether
				retVals.add(CBoolean.get(server.getAllowNether()));
			}
			if(index == 5 || index == -1) {
				//Allow end
				retVals.add(CBoolean.get(server.getAllowEnd()));
			}
			if(index == 6 || index == -1) {
				//World container
				retVals.add(new CString(server.getWorldContainer(), t));
			}
			if(index == 7 || index == -1) {
				//Max player limit
				retVals.add(new CInt(server.getMaxPlayers(), t));
			}
			if(index == 8 || index == -1) {
				//Array of op's
				CArray co = new CArray(t);
				List<MCOfflinePlayer> so = server.getOperators();
				for(MCOfflinePlayer o : so) {
					if(o == null) {
						continue;
					}
					CString os = new CString(o.getName(), t);
					co.push(os, t);
				}
				retVals.add(co);
			}
			if(index == 9 || index == -1) {
				//Array of plugins
				CArray co = new CArray(t);
				MCPluginManager plugManager = server.getPluginManager();
				if(plugManager == null) {
					throw new CRENotFoundException(this.getName()
							+ " could not receive the server plugins. Are you running in cmdline mode?", t);
				}
				List<MCPlugin> plugs = plugManager.getPlugins();
				for(MCPlugin p : plugs) {
					if(p == null) {
						continue;
					}
					CString name = new CString(p.getName(), t);
					co.push(name, t);
				}

				retVals.add(co);
			}
			if(index == 10 || index == -1) {
				//Online Mode
				retVals.add(CBoolean.get(server.getOnlineMode()));
			}
			if(index == 11 || index == -1) {
				//Server port
				retVals.add(new CInt(server.getPort(), t));
			}
			if(index == 12 || index == -1) {
				//Server Ip
				retVals.add(new CString(server.getIp(), t));
			}
			if(index == 13 || index == -1) {
				//Uptime
				long uptime = System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime();
				retVals.add(new CInt(uptime, t));
			}
			if(index == 14 || index == -1) {
				//gcmax
				retVals.add(new CInt((Runtime.getRuntime().maxMemory()), t));
			}
			if(index == 15 || index == -1) {
				//gctotal
				retVals.add(new CInt((Runtime.getRuntime().totalMemory()), t));
			}
			if(index == 16 || index == -1) {
				//gcfree
				retVals.add(new CInt((Runtime.getRuntime().freeMemory()), t));
			}
			if(index == 17 || index == -1) {
				//motd
				retVals.add(new CString(server.getMotd(), t));
			}

			if(retVals.size() == 1) {
				return retVals.get(0);
			}

			CArray ca = new CArray(t);
			for(Construct c : retVals) {
				ca.push(c, t);
			}
			return ca;
		}

		@Override
		public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
			MCServer server = StaticLayer.GetServer();
			int index = -1;
			if(args.length == 0) {
				index = -1;
			} else if(args.length == 1) {
				index = Static.getInt32(args[0], t);
			}

			if(index < -1 || index > 16) {
				throw new CRERangeException(this.getName() + " expects the index to be between -1 and 16 (inclusive)", t);
			}

			ArrayList<Construct> retVals = new ArrayList<Construct>();

			if(index == 0 || index == -1) {
				//Server name
				retVals.add(new CString(server.getServerName(), t));
			}

			if(index == 1 || index == -1) {
				// API Version
				retVals.add(new CString(server.getAPIVersion(), t));
			}
			if(index == 2 || index == -1) {
				// Server Version
				retVals.add(new CString(server.getServerVersion(), t));
			}
			if(index == 3 || index == -1) {
				//Allow flight
				retVals.add(CBoolean.get(server.getAllowFlight()));
			}
			if(index == 4 || index == -1) {
				//Allow nether
				retVals.add(CBoolean.get(server.getAllowNether()));
			}
			if(index == 5 || index == -1) {
				//Allow end
				retVals.add(CBoolean.get(server.getAllowEnd()));
			}
			if(index == 6 || index == -1) {
				//World container
				retVals.add(new CString(server.getWorldContainer(), t));
			}
			if(index == 7 || index == -1) {
				//Max player limit
				retVals.add(new CInt(server.getMaxPlayers(), t));
			}
			if(index == 8 || index == -1) {
				//Array of op's
				CArray co = new CArray(t);
				List<MCOfflinePlayer> so = server.getOperators();
				for(MCOfflinePlayer o : so) {
					if(o == null) {
						continue;
					}
					CString os = new CString(o.getName(), t);
					co.push(os, t);
				}
				retVals.add(co);
			}
			if(index == 9 || index == -1) {
				//Array of plugins
				CArray co = new CArray(t);
				MCPluginManager plugManager = server.getPluginManager();
				if(plugManager == null) {
					throw new CRENotFoundException(this.getName()
							+ " could not receive the server plugins. Are you running in cmdline mode?", t);
				}
				List<MCPlugin> plugs = plugManager.getPlugins();
				for(MCPlugin p : plugs) {
					if(p == null) {
						continue;
					}
					CString name = new CString(p.getName(), t);
					co.push(name, t);
				}

				retVals.add(co);
			}
			if(index == 10 || index == -1) {
				//Online Mode
				retVals.add(CBoolean.get(server.getOnlineMode()));
			}
			if(index == 11 || index == -1) {
				//Server port
				retVals.add(new CInt(server.getPort(), t));
			}
			if(index == 12 || index == -1) {
				//Server Ip
				retVals.add(new CString(server.getIp(), t));
			}
			if(index == 13 || index == -1) {
				//Uptime
				long uptime = System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime();
				retVals.add(new CInt(uptime, t));
			}
			if(index == 14 || index == -1) {
				//gcmax
				retVals.add(new CInt((Runtime.getRuntime().maxMemory()), t));
			}
			if(index == 15 || index == -1) {
				//gctotal
				retVals.add(new CInt((Runtime.getRuntime().totalMemory()), t));
			}
			if(index == 16 || index == -1) {
				//gcfree
				retVals.add(new CInt((Runtime.getRuntime().freeMemory()), t));
			}
			if(index == 17 || index == -1) {
				//motd
				retVals.add(new CString(server.getMotd(), t));
			}

			if(retVals.size() == 1) {
				return retVals.get(0);
			}

			CArray ca = new CArray(t);
			for(Construct c : retVals) {
				ca.push(c, t);
			}
			return ca;
		}

Replace Generic With Diamond ArrayList<Construct> with ArrayList<> in method public exec(t Target, env Environment, args Construct...) : Construct from class com.laytonsmith.core.functions.Minecraft.get_server_info


	@Override
	public Collection<MCItemStack> getDrops() {
		Collection<MCItemStack> collection = new ArrayList<>();
		for(ItemStack is : b.getDrops()) {
			collection.add(new BukkitMCItemStack(is));
		}
		return collection;
	}

	@Override
	public Collection<MCItemStack> getDrops() {
		Collection<MCItemStack> collection = new ArrayList<MCItemStack>();
		for(ItemStack is : b.getDrops()) {
			collection.add(new BukkitMCItemStack(is));
		}
		return collection;
	}

Replace Generic With Diamond ArrayList<MCItemStack> with ArrayList<> in method public getDrops() : Collection<MCItemStack> from class com.laytonsmith.abstraction.bukkit.blocks.BukkitMCBlock


	@Override
	public Collection<MCItemStack> getDrops(MCItemStack tool) {
		Collection<MCItemStack> collection = new ArrayList<>();
		for(ItemStack is : b.getDrops(((BukkitMCItemStack) tool).asItemStack())) {
			collection.add(new BukkitMCItemStack(is));
		}
		return collection;
	}

	@Override
	public Collection<MCItemStack> getDrops(MCItemStack tool) {
		Collection<MCItemStack> collection = new ArrayList<MCItemStack>();
		for(ItemStack is : b.getDrops(((BukkitMCItemStack) tool).asItemStack())) {
			collection.add(new BukkitMCItemStack(is));
		}
		return collection;
	}

Replace Generic With Diamond ArrayList<MCItemStack> with ArrayList<> in method public getDrops(tool MCItemStack) : Collection<MCItemStack> from class com.laytonsmith.abstraction.bukkit.blocks.BukkitMCBlock