I am trying to utilize a .properties file to get a list of strings and transform them into an array to be added with javassist later in my program. However, I keep getting an arrayoutofboundsexception 0 with the following data in properties:
modify.spells.in.
void configure(Properties options)
{
String nameSpace;
ArrayList<String> spellList = new ArrayList<String>();
String[] tokens;
for(String key : options.stringPropertyNames()) {
if(key.startsWith("modify.spells.in.")) {
nameSpace = key.substring(17);
tokens = options.getProperty(key).split(",");
for(String token : tokens) {
if(spellList.contains(nameSpace + "." + token)) {
continue;
}
spellList.add(nameSpace + "." + token);
}
}
}
if(spellList.size() == 0) {
itemSpells = new String[]{
"Fire1", "Fire2", "Fire3", "Lit1", "Lit2", "Lit3", "Ice1", "Ice2",
"Ice3"
};
for(int i = 0; i < itemSpells.length; i++) {
itemSpells[i] = "com.ragdoll.spells." + itemSpells[i];
}
} else {
itemSpells = spellList.toArray(new String[0]);
}
The properties file string is as follows:
modify.spells.in.com.ragdoll.spells=Fire3,Lit3,Ice3
The expression to edit the existing code is as follows:
I have a HookManager to add in interceptSpells(itemSpells);
private void interceptSpells(String[] classes) throws NotFoundException, CannotCompileException
{
CtClass c;
ClassPool cp = HookManager.getInstance().getClassPool();
CtMethod m = null;
// CAST -
for(int i = 0; i < classes.length; i++) {
final int j = i;
c = cp.get(classes[i]);
m = c.getDeclaredMethods("doEffect")[0];
m.instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
if (m.getMethodName().equals("addSpellEffect")) {
logger.info("Intercepting (1) " + classes[j]);
m.replace(
"$_ = $proceed($$);"
+ "com.rev.spellmod.getInstance().addSpellEffectCaster($0, eff, performer); "
);
} else if(m.getMethodName().equals("improvePower")) {
logger.info("Intercepting (2) " + classes[j]);
m.replace(
"$_ = $proceed($$);"
+ "com.rev.spellmod.getInstance().replaceSpellEffectCaster($0, eff, performer); "
);
}
}
});
I feel as though I have declared the namespace and am splitting the list correctly. If I add just one spell in the .properties file, it works. As soon as I add more than one it points back to the intercept code with the arrayoutofboundsexception. If I leave it blank, it also works with the pre-determined spells, but I was hoping to be able to modify the spells on the go rather the recompiling each time.
Any thoughts or help would be appreciated!
I realized the doEffect was the issue. Some of the spells inherited doEffect from another class and so that was producing the ArrayOutOfBoundsException not the parsing of the string which was correct.
Related
I've written a Java code to validate if a (javascript based) code uses same variable name for two "for loops" (only applicable to the variable initialized inside for statement). For example: this is a correct code
for(var a=0;a<b;a++)
{
for(var b=0;b<c;b++)
{
TheApplication().Trace("ciclo for a2: " + a);
}
}
This code isn't correct:
for(var a=0;a<b;a++)
{
for(var a=0;a<c;a++)
{
TheApplication().Trace("ciclo for a2: " + a);
}
}
Please note that: this isn't actually a javaScript but it's called eScript (it's in Oracle based packaged product called Siebel), hence I cannot use javaScript compilers for evaluation.
Below is the code I've written, but I see a problem, that incase if there are two independent loops inside first "for loop" and they use same variable names in the FOR statement, still my code throws error.
for(var a=0;a<b;a++)
{
for(var b=0;b<c;b++)
{//First loop
}
for(var b=0;b<c;b++)
{//this is not a problem
}
}
Here's my code:
public static void main(String[] args) {
// TODO code application logic here
ArrayList<String> ScriptList = new ArrayList<String>();
ScriptList.add("function ForLoop(){try{var b = 20;var c = 10; TheApplication().TraceOn(\"//sbsiebASLog//logs//FedeGestioneNP.log\",\"Allocation\",\"All\");for(var a=0;a<b;a++){TheApplication().Trace(\"ciclo for a1: \" + a);for(var a=0;a<c;a++){TheApplication().Trace(\"ciclo for a2: \" + a);}} }catch(e){}finally{}}");
ScriptList.add("function ForLoop(){try{var b = 20;var c = 10; TheApplication().TraceOn(\"//sbsiebASLog//logs//FedeGestioneNP.log\",\"Allocation\",\"All\");for(var a=0;a<b;a++){TheApplication().Trace(\"ciclo for a1: \" + a);for(var b=0;a<c;a++){TheApplication().Trace(\"ciclo for a2: \" + a);}} }catch(e){}finally{}}");
for(int i =0;i<ScriptList.size();i++)
{
CheckPattern(ScriptList.get(i))
}
}
private static CheckPattern(String Script)
{
boolean bAllVerified = false;
Script = Script.replaceAll("for( var", "for(var");
Script = Script.replaceAll("for ( var", "for(var");
Script = Script.replaceAll("for (var", "for(var");
String sRemaining = Script;
while(!bAllVerified)
{
if(!sRemaining.contains("For(int"))
bAllVerified = true;
else
sRemaining = CheckForLoop(sRemaining);
}
}
private static String CheckForLoop(String string)
{
String rstring = "";
int iFirst = string.indexOf("for(var");
int iCheckIndex = CheckCloseCurlBracket(string,iFirst-("for(var").length());
rstring = string.substring(iCheckIndex);
return rstring;
}
private static int CheckCloseCurlBracket(String string,int iFirst)
{
int iRet=string.length();
ArrayList<Integer> iForIndex = new ArrayList<Integer>();
if(iRet==0)
return iRet;
int iOpen = 0;
int iClose = 0;
int iCloseIndex=0;
int iCountFor=1;
for(int i=iFirst;i<string.length();i++)
{
if(string.charAt(i)=='{')
iOpen++;
else if(string.charAt(i)=='}')
{
iClose++;
iRet = i;
if(iOpen == iClose)
break;
}
if("for(var".equals(string.substring(i,i+("for(var").length())))
{
iCountFor++;
iForIndex.add(i);
}
}
fProcessPattern(string, iFirst,iRet,iCountFor,iForIndex);
return iRet;
}
private static void fProcessPattern(String string, int iFirst, int iLast, int iCountFor, ArrayList<Integer> iForIndex)
{
String sActualString = string.substring(iFirst,iLast);
ArrayList<String> ForList = new ArrayList<String>();
for(int i=0; i<iCountFor;i++)
{
String temp = sActualString.substring(iForIndex.get(i),("for(var").length()+sActualString.indexOf("="));
ForList.add(temp);
}
for(int i = 0; i < ForList.size(); i++)
{
for(int j = i + 1; j < ForList.size(); j++)
{
if(j != i && (ForList.get(j) == null ? ForList.get(i) == null : ForList.get(j).equals(ForList.get(i)))) {
messagebox("Duplicate");
}
}
}
}
I want to make a configuration to store items, however, when I was making the paths to get the values, something wrong happened.
HashMap<String, Text> sections;
private void loadKeys() {
List<String> list = new ArrayList<>();
for (String s : sections.keySet()) {
Text te = sections.get(s);
String changeable = s.substring(0, s.length() - 1);
for (int i = 0; i < te.lines(); i++) {
String line = te.getLine(i);
while (line.startsWith(" ")) {
line = line.substring(2);
}
if (!line.startsWith("-")) {
if (line.endsWith(":")) {
changeable = changeable + "." + line.substring(0, line.length() - 1);
} else {
list.add(changeable + "." + line);
}
}
}
}
for (String s : list) {
System.out.println(s);
}
}
Text.java
public class Text {
private List<String> lines = new ArrayList<>();
public Text(String txt) {
if (txt.contains("\n")) {
for (String s : txt.split("\n")) {
lines.add(s);
}
} else {
lines.add(txt);
}
}
public int lines() {
return lines.size();
}
public String getLine(int line) {
return lines.get(line);
}
#Override
public String toString() {
String string = "";
for (String s : lines) {
if (string.equals("")) {
string = s;
} else {
string = string + "\n" + s;
}
}
return string;
}
}
File:
Test11:
Test12:
Test13: 'test'
Test14: 'test2'
Test15: teste
Test16:
Test17: "test test"
The output I want:
Test11.Test12.Test13: 'test'
Test11.Test12.Test14: 'test2'
Test11.Test15: teste
Test11.Test16.Test17: "test test"
What I got with the code above:
Test11.Test12.Test13: 'test'
Test11.Test12.Test14: 'test2'
Test11.Test12.Test15: teste
Test11.Test12.Test16.Test17: "test test"
Test12 is being repeated. Can you help me have what I want? Thanks in advance
It is pretty easy. All you need is just keep current level depth and level name. You can do it via recursion or using queue.
public static Map<String, String> readProperties(Path path) throws IOException {
final class Level {
private final String name;
private final int offs;
public Level(String name, int offs) {
this.name = name;
this.offs = offs;
}
}
Map<String, String> map = new LinkedHashMap<>();
// contains all root items for current one with it's offset, to detecl that current level is sub level or parent
Deque<Level> levels = new LinkedList<>();
Pattern pattern = Pattern.compile("(?<offs>\\s*)(?<key>[^:]+)\\s*:\\s*(?<value>.*)\\s*");
Files.lines(path)
.map(pattern::matcher)
.filter(Matcher::matches)
.forEach(matcher -> {
int offs = matcher.group("offs").length();
// remove parent levels until reach the parent of current level
while (!levels.isEmpty() && levels.peekLast().offs >= offs) {
levels.removeLast();
}
String key = matcher.group("key");
String value = matcher.group("value");
if (value.isEmpty())
levels.add(new Level(key, offs));
else
map.put(levels.stream().map(level -> level.name).collect(Collectors.joining(".")) + '.' + key, value);
});
return map;
}
I am trying to add a header to the beginning of each page in Java. I am printing my console output to a file as well. All of the examples I can find are for PDF files but I need to print to a text file for later data transfer and use iText which I cannot use. Any suggestions would be awesome. Here is how I am printing so far:
PrintStream out = new PrintStream(new FileOutputStream("example.txt"));
System.setOut(out);
I'm assuming you're going to use fixed-width characters (anything else would be significantly more complicated..)
Here is a naive implementation, you can tweak it from here. I think it works pretty decently but you should test it thoroughly, also handle the case when a word is longer than a line and maybe a better exception for when a header is longer than the page:
public class PagePrinter {
private final PrintStream printer;
private final int pageWidth;
private final int pageLength;
private int currWidth = 0;
private int currLine = 0;
private int currPage = 1;
private boolean inPageHeader = false;
/**
* #param printer
* - print stream to print to
* #param pageWidth
* - in characters
* #param pageLength
* - in lines, includes the length of the header
*/
public PagePrinter(PrintStream printer, int pageWidth, int pageLength) {
this.printer = printer;
this.pageLength = pageLength;
this.pageWidth = pageWidth;
}
public void print(String str) {
// replace tabs with spaces
// may need to replace other chars that don't translate to 1 char when printed
str = str.replace("\t", " ");
// split would drop a trailing delimiter so concat extra
String[] lines = str.concat("\r\n#").split("\\r?\\n");
// print first
printWords(lines[0]);
// print rest excluding the extra
for (int i = 1; i < lines.length - 1; i++) {
// re-add delimiter (but keeping track of its affect on the page)
newline();
printWords(lines[i]);
}
}
private void printWords(String str) {
// split would drop a trailing delimiter so concat extra
String[] words = str.concat(" #").split(" ");
printWord(words[0]);
for (int i = 1; i < words.length - 1; i++) {
// re-add delimiter (but keeping track of its affect on the page)
if (currWidth < pageWidth) {
printer.print(" ");
currWidth++;
}
printWord(words[i]);
}
}
/** The smallest unit of appending to the document, */
private void printWord(String word) {
// determines when to print a header
if (currLine == 0 && !inPageHeader) {
printPageHeader();
}
int remainingSpaceOnLine = pageWidth - currWidth;
if (word.length() < remainingSpaceOnLine) {
printer.print(word);
currWidth += word.length();
} else if (word.length() < pageWidth) {
newline();
printWord(word);
} else {
// FIXME word is longer than the page width
// maybe split it with a hyphen and addWord() the parts
throw new RuntimeException("Word '" + word + "' is longer than line!");
}
}
public void newline() {
currLine++;
if (currLine >= pageLength) {
newPage();
} else {
currWidth = 0;
printer.println();
}
}
public void newPage() {
if (inPageHeader) {
throw new RuntimeException("Page header is longer than the page!!!");
}
currWidth = 0;
currLine = 0;
currPage++;
printer.println();
}
private void printPageHeader() {
inPageHeader = true;
myPageHeader();
inPageHeader = false;
}
protected void myPageHeader() {
print("----- Page " + currPage + " -----\n");
}
public static void main(String[] args) {
PagePrinter test = new PagePrinter(System.out, 40, 10);
test.print("\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because..."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because..."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because.."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because...");
test.newPage();
test.print("This is a new page!");
test.newline();
test.print("This is a newline even though part would've fit on the previous!");
}
}
I'm trying to make a small utility mod for Minecraft 1.7.10 in which you place a list of IDs in a config file and the mod removes their recipes from the game. I've got the config file working but I don't seem to be able to remove the recipes.
My method for removing the recipes is one I have seen in a couple of forum threads elsewhere and slightly modified to work with my config file (or not work in my case).
Here is the method:
private void removeRecipes(String toDelete)
{
ArrayList<?> recipes = (ArrayList<?>) CraftingManager.getInstance().getRecipeList();
ItemStack recipeResult = null;
ItemStack resultItem = new ItemStack((Item)Item.itemRegistry.getObject(toDelete));
resultItem.stackSize = 1;
resultItem.setItemDamage(0);
for (int scan = 0; scan < recipes.size(); scan++)
{
IRecipe tmpRecipe = (IRecipe) recipes.get(scan);
if (tmpRecipe instanceof ShapedRecipes)
{
ShapedRecipes recipe = (ShapedRecipes)tmpRecipe;
recipeResult = recipe.getRecipeOutput();
recipeResult.stackSize = 1;
recipeResult.setItemDamage(0);
}
if (tmpRecipe instanceof ShapelessRecipes)
{
ShapelessRecipes recipe = (ShapelessRecipes)tmpRecipe;
recipeResult = recipe.getRecipeOutput();
recipeResult.stackSize = 1;
recipeResult.setItemDamage(0);
}
if (ItemStack.areItemStacksEqual(resultItem, recipeResult))
{
System.out.println("[RecipeRemover] Removed Recipe: " + recipes.get(scan) + " -> " + recipeResult);
recipes.remove(scan);
}
}
}
Something was obviously a bit out with my code, but this is a much more efficient approach which works (and should work universally):
#SuppressWarnings("unchecked")
private void removeRecipes(String toDelete)
{
ItemStack resultItem = new ItemStack((Item)Item.itemRegistry.getObject(toDelete));
resultItem.stackSize = 1;
resultItem.setItemDamage(0);
List<IRecipe> recipes = CraftingManager.getInstance().getRecipeList();
for (int i = 0; i < recipes.size(); i++)
{
IRecipe tmpRecipe = recipes.get(i);
ItemStack recipeResult = tmpRecipe.getRecipeOutput();
if(recipeResult != null)
{
recipeResult.stackSize = 1;
recipeResult.setItemDamage(0);
}
if (ItemStack.areItemStacksEqual(resultItem, recipeResult))
{
recipes.remove(i--);
}
}
}
How can I get a class reference/TypeElement of a specific identifier at compile time in Java?
Say I have the following source file, and I want to get a reference to the Pixel class so I can get a list of its member fields.
package com.foo.bar;
class Test {
class Pixel {
int x,y,r,g,b;
}
Pixel saturate(Pixel p, int value) {...}
}
The Pixel class definition could be nested inside the Test class, or included from a different package where the source is not available.
I am using the javax.tools API to compile the source files, and I define visitor methods so I can view the arguments to each function. The arguments of a function can be iterated using VariableTree var : node.getParameters(), but the type information from var.getType() only triggers visitIdentifier for class names. This identifier is only the simple name Pixel, not the fully-qualified com.foo.bar.Pixel.
I need a way to reverse this identifier into either Pixel.class or into the TypeElement for the definition of the Pixel class, or into the fully-qualified com.foo.bar.Pixel string so I can then use a ClassLoader on it.
A crude way would be to record all class definitions and then try to do compile-time type lookup, but this wouldn't work for externally-defined classes.
As far as I remember var.getType().toString() returns you the fully qualified class name. Unfortunately I cannot check it right now, but try it yourself.
Yes, I know, it is a very bad style to use toString() for something except logging but it seems they have not given us other choice.
I ended up creating my own class lookup tool. For those that are interested, I'll include it here.
Call this with the path name of every source file to be included in the search:
public void populateClassDefinitions(String path) {
Iterable<? extends JavaFileObject> files = fileManager.getJavaFileObjects(path);
CompilationTask task =
compiler.getTask(null, fileManager, diagnosticsCollector, null, null, files);
final JavacTask javacTask = (JavacTask) task;
parseResult = null;
try {
parseResult = javacTask.parse();
} catch (IOException e) {
e.printStackTrace();
return;
}
for (CompilationUnitTree tree : parseResult) {
tree.accept(new TreeScanner<Void, Void>() {
#Override
public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
currentPackage = "";
ExpressionTree packageName = node.getPackageName();
if (packageName != null) {
String packageNameString = String.valueOf(packageName);
if (packageNameString.length() > 0) {
currentPackage = packageNameString;
}
}
TreeScanner<Void, String> visitor = new TreeScanner<Void, String>() {
#Override
public Void visitClass(ClassTree node, String packagePrefix) {
if (classDefinitions.get(currentPackage) == null) {
classDefinitions.put(currentPackage, new HashMap<String, ClassTree>());
}
classDefinitions.get(currentPackage).put(packagePrefix + node.getSimpleName(), node);
return super.visitClass(node, packagePrefix + node.getSimpleName() + ".");
}
};
for (Tree decls : node.getTypeDecls()) {
decls.accept(visitor, "");
}
return super.visitCompilationUnit(node, p);
}
}, null);
}
}
Call this to search for classes.
/**
* Lookup the definition of a class.
*
* Lookup order: 1. Search in the current file: within the current class scope upwards to the
* root. 2. Search laterally across files with the same package value for implicitly included
* classes. 3. Check all import statements.
*
* #param pack
* Current package ex "edu.illinois.crhc"
* #param scope
* Current scope ex "Test.InnerClass"
* #param identifier
* The partial class name to search for
* #return ClassTree the definition of this class if found
*/
ClassLookup lookupClass(CompilationUnitTree packTree, String scope, String identifier) {
dumpClassTable();
String pack = packTree.getPackageName().toString();
System.out.println("Looking for class " + pack + " - " + scope + " - " + identifier);
// Search nested scope and within same package
HashMap<String, ClassTree> packClasses = classDefinitions.get(pack);
if (packClasses != null) {
String[] scopeWalk = scope.split("\\.");
for (int i = scopeWalk.length; i >= 0; i--) {
StringBuilder scopeTest = new StringBuilder();
for (int j = 0; j < i; j++) {
scopeTest.append(scopeWalk[j] + ".");
}
scopeTest.append(identifier);
System.out.println("Testing scope " + pack + " - " + scopeTest.toString());
if (packClasses.containsKey(scopeTest.toString())) {
return new ClassLookup(packClasses.get(scopeTest.toString()), pack.replace(".", "/")
+ "/" + scopeTest.toString().replace(".", "$"));
}
}
}
/*
* Check if fully-qualified identifier (foo.bar.Widget) is used. This needs to search all
* combinations of package and class nesting.
*/
StringBuilder packTest = new StringBuilder();
String[] qualifiedName = identifier.split("\\.");
for (int i = 0; i < qualifiedName.length - 1; i++) {
packTest.append(qualifiedName[i]);
if (i != qualifiedName.length - 2) {
packTest.append(".");
}
}
String clazz = qualifiedName[qualifiedName.length - 1];
System.out.println("Testing absolute identifier: " + packTest.toString() + " " + clazz);
if (classDefinitions.containsKey(packTest.toString())) {
HashMap<String, ClassTree> foundPack = classDefinitions.get(packTest.toString());
if (foundPack.containsKey(clazz)) {
return new ClassLookup(foundPack.get(clazz), packTest.toString().replace(".", "/") + "/"
+ clazz.replace(".", "$"));
}
}
/*
* Search import statements. Last identifier segment must be class name. Search all of the
* packages for the identifier by splitting off the class name. a.b.c.Tree Tree.Branch
* Tree.Branch.Leaf
*/
for (ImportTree imp : currentPackTree.getImports()) {
pack = imp.getQualifiedIdentifier().toString();
System.out.println(pack);
String[] importName = pack.split("\\.");
// Split off class name.
// TODO: (edge case) no package
StringBuilder importTest = new StringBuilder();
for (int i = 0; i < importName.length - 1; i++) {
importTest.append(importName[i]);
if (i != importName.length - 2) {
importTest.append(".");
}
}
// See if the last import segment is * or matches the first segment of the identifier.
System.out.println("Testing globally " + importTest.toString() + " - " + identifier);
if (classDefinitions.containsKey(importTest.toString())) {
HashMap<String, ClassTree> foundPack = classDefinitions.get(importTest.toString());
String[] identifierParts = identifier.split(".");
String importClass = importName[importName.length-1];
if (importClass.equals("*") || identifierParts[0].equals(importClass)) {
if (foundPack.containsKey(identifier)) {
return new ClassLookup(foundPack.get(identifier), importTest.toString().replace(".", "/")
+ "/" + identifier.replace(".", "$"));
}
}
}
}
return null;
}