I'm parsing a text file that is being mapped to some java code like such:
public void eval(Node arg)
{
if(arg.data.equals("rand"))
{
moveRandomly();
}
else if(arg.data.equals("home"))
{
goHome();
}
else if(arg.data.equals("iffood"))
{
ifFoodHere(arg.left, arg.right);
}//snip..
This is going to need to be re-evaluated about a thousand times and I'd rather not have to traverse the whole thing every time. Is there any way to make this traversal once and then have it be a function that is called every other time?
You could make a Map of Runnables:
Map<String, Runnable> methods = new HashMap<String, Runnable>();
methods.put("rand", new Runnable()
{
public void run()
{
moveRandomly();
}
});
...
then in your method
public void eval(Node arg)
{
Runnable command = methods.get(arg.data);
command.run();
}
Create an anonymous inner class.
Something like:
public Callable<Void> eval(Node arg)
{
if(arg.data.equals("rand"))
{
return new Callable<Void>{ public Void call() { moveRandomly(); return null; } };
}
...
}
Callable<Void> f = eval(a);
f.call();
If you know all the arguments/commands you can expect, i might do it like this:
enum Args {
home, rand, iffood;
private Method method;
private Args () {
try {
this.method = Commands.class.getMethod(this.name(), Node.class);
} catch (final Exception e) {}
}
public void invoke (final Node args)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
this.method.invoke(null, args);
}
public static Args valueOf (final Node arg) {
return valueOf(arg.data);
}
public static void eval (final Node arg)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
valueOf(arg).invoke(arg);
}
}
Command implementations are:
class Commands {
public static void home (final Node arg) {
goHome(); // Call the implementation
// or simply make these bodies the implementations.
}
public static void iffood (final Node arg) {
ifFoodHere(arg.left, arg.right);
}
public static void rand (final Node arg) {
moveRandom();
}
//...
}
your eval() then becomes, simply:
try {
Args.eval(arg);
} catch (IllegalArgumentException e) {
// Handle unknown arg.data
}
Related
New to this topic and right now I'm stuck at a brick wall. I have 2 classes, parent class: Controller.java and subclass: GreenhouseControls.java. I need to serialize a GreenhouseControls object but also an instance variable (eventList) from its superclass Controller.java.
My serialization happens when an inner class of GreenhouseControls.java throws a custom ControllerException, which is caught in the main method. Before terminating the program, the GreenhouseControls object should be saved (including the field from its superclass).
Why is a NotSerializableException thrown by the inner class WindowMalfunction of GreenhouseControls? Anyone have any ideas, as I am seriously stuck?
What I tried is the following:
Implement serializable on Controller.java. This is because if the superclass is serializable, then subclass is automatically serializable, however this throws java.io.NotSerializableException: GreenhouseControls$WindowMalfunction, (WindowMalfunction is the inner class that throws the initial exception to begin the serialization processs).
Implement serializable on GreenhouseControls.java and implement custom serialization by overriding writeObject() and readObject() to save the field from the superclass. This approach yet again throws the same exception as the approach 1.
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(super.eventList);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
Object obj = in.readObject();
List<Event> x = cast(obj);
super.eventList = x;
}
Controller.java
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Controller {
// THIS IS THE VARIABLE I NEED TO SAVE
protected List<Event> eventList = new ArrayList<Event>();
public void addEvent(Event c) {
eventList.add(c);
}
public void run() throws ControllerException {
while (eventList.size() > 0)
// Make a copy so you're not modifying the list
// while you're selecting the elements in it:
for (Event e : new ArrayList<Event>(eventList))
if (e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
public static void shutDown() { }
}
GreenhouseControls.java class (note I have removed the inner classes and other code from it and only left related info)
public class GreenhouseControls extends Controller implements Serializable {
private int errorcode = 0;
public class WindowMalfunction extends Event {
public WindowMalfunction(long delayTime) {
super(delayTime);
}
public void action() throws ControllerException {
windowok = false;
throw new ControllerException("Window malfunction");
}
public String toString() {
return "Window malfunction";
}
}
public class PowerOut extends Event {
public PowerOut(long delayTime) {
super(delayTime);
}
public void action() throws ControllerException {
poweron = false;
throw new ControllerException("Power out");
}
public String toString() {
return "Power out";
}
}
// Various other inner classes that extend event exist
public static void serializeObject(GreenhouseControls gc) {
FileOutputStream fileOut;
ObjectOutputStream out;
try {
fileOut = new FileOutputStream("dump.out");
out = new ObjectOutputStream(fileOut);
out.writeObject(gc);
System.out.println("WERRROR code: " + gc.getError());
out.close();
fileOut.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(super.eventList);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
Object obj = in.readObject();
List<Event> x = cast(obj);
super.eventList = x;
}
#SuppressWarnings("unchecked")
public static <T extends List<?>> T cast(Object obj) {
return (T) obj;
}
public int getError() {
return errorcode;
}
public Fixable getFixable(int errorcode) {
switch (errorcode) {
case 1:
return new FixWindow();
case 2:
return new PowerOn();
default:
return null;
}
}
public static void main(String[] args) {
GreenhouseControls gc = null;
try {
String option = args[0];
String filename = args[1];
if (!(option.equals("-f")) && !(option.equals("-d"))) {
System.out.println("Invalid option");
printUsage();
}
// gc = new GreenhouseControls();
if (option.equals("-f")) {
gc = new GreenhouseControls();
gc.addEvent(gc.new Restart(0, filename));
}
gc.run();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid number of parameters");
printUsage();
} catch (ControllerException e) {
String errormsg;
if (e.getMessage().equals("Window malfunction")) {
gc.errorcode = 1;
errormsg = "Window malfunction event occurred Error code: " + gc.errorcode;
} else {
gc.errorcode = 2;
errormsg = "Power out event occurred Error code: " + gc.errorcode;
}
logError(errormsg);
serializeObject(gc);
gc.displayEventList();
shutDown();
}
}
}
Event.java
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() { // Allows restarting
eventTime = System.currentTimeMillis() + delayTime;
}
public boolean ready() {
return System.currentTimeMillis() >= eventTime;
}
public abstract void action() throws ControllerException;
Event has to be Serializable too.
Change
public abstract class Event {
to
public abstract class Event implements Serializable {
I am trying to write a method that can take in a String classname and a String value, and return the value represented as that String.
Example inputs:
parse("java.lang.String", "abc") -> String "ABC"
parse("java.lang.Boolean", "FALSE") -> Boolean FALSE
parse("java.lang.Integer", "123") -> Integer 123
parse("com.me.Color", "RED") -> enum Color.RED
I have found that if I use an if block containing assignableFrom calls, I can achieve this. But would prefer writing something more extendable, so it isn't as difficult to add a new parser tomorrow.
This is what I have now:
String stringClassName = //stringified full class name
String value = //value to parse
Class<?> fieldType = Class.forName(stringClassName)
if (fieldType.isAssignableFrom(String.class)) {
return value;
} else if (fieldType.isAssignableFrom(Boolean.class)) {
return Util.toBoolean(value);
} else if (fieldType.isEnum()) {
return Util.toEnum(fieldType, value);
} else {
// throw exception
}
There are multiple ways to do this. For example:
You could have an interface called Parser
package example;
public interface Parser {
boolean canParse(String fullQualifiedClassName);
Object parse(String fullQualifiedClassName, String value) throws ParseException;
class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
public ParseException(Exception cause) {
super(cause);
}
}
}
And all your Default-Implementations in an Enum or statically defined in another way:
package example;
public enum DefaultParser implements Parser {
STRING {
#Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, String.class);
}
#Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value;
}
},
ENUM {
#Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Enum.class);
}
#Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
final Class<? extends Enum> clazz;
try {
clazz = (Class<? extends Enum>) Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
throw new ParseException(e);
}
return Enum.valueOf(clazz, value);
}
},
BOOLEAN {
#Override
public boolean canParse(String fullQualifiedClassName) {
return isClassAssignableFromClassName(fullQualifiedClassName, Boolean.class);
}
#Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return value.toLowerCase().equals("true");
}
};
private static boolean isClassAssignableFromClassName(String fullQualifiedClassName, Class<?> clazz) {
try {
return clazz.isAssignableFrom(Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException e) {
return false;
}
}
}
And a ParentParser Implementation that combines multiple Parsers into one:
package example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ParentParser implements Parser {
private final List<Parser> parsers;
public ParentParser() {
this.parsers = new ArrayList<>();
this.parsers.addAll(Arrays.asList(DefaultParser.values()));
}
public void register(Parser parser) {
this.parsers.add(parser);
}
#Override
public boolean canParse(String fullQualifiedClassName) {
return findParser(fullQualifiedClassName).isPresent();
}
#Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
return findParser(fullQualifiedClassName)
.orElseThrow(() -> new ParseException("no registered parser found for class=" + fullQualifiedClassName))
.parse(fullQualifiedClassName, value);
}
private Optional<Parser> findParser(String fullQualifiedClassName) {
return this.parsers.stream().filter(parser -> parser.canParse(fullQualifiedClassName)).findAny();
}
}
Which you can then use like this:
package example;
import example.Parser.ParseException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
}
}
And you could add more parsers, for example a parser using Jackson (JSON):
package example;
import com.fasterxml.jackson.databind.ObjectMapper;
import example.Parser.ParseException;
import java.io.IOException;
public class Example {
public static void main(String[] args) throws ParseException {
final ParentParser parser = new ParentParser();
System.out.println(parser.parse("java.lang.String", "hello world"));
System.out.println(parser.parse("java.lang.Boolean", "true"));
System.out.println(parser.parse("java.time.DayOfWeek", "TUESDAY"));
parser.register(new JacksonParser());
System.out.println(parser.parse("java.util.Map", "{\"key\":\"value\"}"));
}
private static class JacksonParser implements Parser {
private static final ObjectMapper MAPPER = new ObjectMapper();
#Override
public boolean canParse(String fullQualifiedClassName) {
final Class<?> clazz;
try {
clazz = Class.forName(fullQualifiedClassName);
} catch (ClassNotFoundException e) {
return false;
}
return MAPPER.canDeserialize(MAPPER.constructType(clazz));
}
#Override
public Object parse(String fullQualifiedClassName, String value) throws ParseException {
try {
return MAPPER.readValue(value, Class.forName(fullQualifiedClassName));
} catch (ClassNotFoundException | IOException e) {
throw new ParseException(e);
}
}
}
}
Note that this can of course be optimized depending on your needs.
If your Parser-Implementations can only parse a static List of Types and there is only one Parser-Implementation per Class, you should change the List<Parser> to Map<Class<?>, Parser> and change the register-Method to register(Class<?> clazz, Parser parser) for example
You can write a generic solution using reflection apis in java.
That would reduce a lot amount of code and would be more extensible.
Also not there is a separate processing required for enum types.
I have covered the basic cases in the code shown below.
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object instance1 = parse("java.lang.String", "abc", false);
Object instance2 = parse("java.lang.Boolean", "FALSE", false);
Object instance3 = parse("java.lang.Integer", "123", false);
Object instance4 = parse("com.me.Color", "RED", true);
}
private static Object parse(String className, String argument, boolean isEnum) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
if (isEnum) {
Object value = Enum.valueOf((Class<? extends Enum>) Class.forName(className), argument);
//System.out.println(value);
return value;
} else {
return parse(className, new Object[]{argument}, isEnum);
}
}
private static Object parse(String className, Object[] arguments, boolean isEnum) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(arguments);
//System.out.println(object);
return object;
}
I need to add the following class at run time:
public class MapperFunction extends Mapper<Integer, String, String, Integer> {
public MapperFunction(InputBlock s) {
super(s);
}
#Override
protected void map(Integer key, String value) {
Pattern pattern = Pattern.compile("[a-zA-Z]+");
Matcher matcher;
String str = value;
if (!str.equals("")) {
matcher = pattern.matcher(str);
while (matcher.find()) {
String word = matcher.group();
if (!MapperOut.containsKey(word))
MapperOut.put(word, 1);
else
MapperOut.put(word, (Integer) MapperOut.get(word) + 1);
}
}
}
}
or add the method map() to class during run time. I read the following question on stackoverflow Extending or adding new classes at runtime in Java
but I think my case differs a bit, anyway this is the parent class Mapper:
public abstract class Mapper<keyIn, valueIn, keyOut, valueOut> implements Runnable {
private RecordReader recordReader;
static AtomicInteger numberOfThreadsPerMapper = new AtomicInteger(2);
static Map<Object, Object> MapperOut = null;
static {
MapperOut = Collections.synchronizedMap(new TreeMap<>());
}
Mapper(InputBlock s) {
recordReader = new LineRecordReader(s);
}
protected abstract void map(keyIn key, valueIn value) throws IOException, InterruptedException;
#Override
public void run() {
run2();
}
public void run2() {
try {
while (recordReader.hasNext()) {
map((keyIn) recordReader.getKey(), (valueIn) recordReader.getValue());
}
// System.out.println("Thread out." + numberOfThreads.getAndIncrement());
} catch (Exception e) {
// e.printStackTrace();
}
}
}
Note that the parent class Mapper extends Thread.
My second question: if I can do that how can I create instance from it? Now this my call to create:
#Override
public void update(Observable o, Object arg) {
executorService.submit(new MapperFunction((InputBlock) arg));
}
My last question: if all that can happen is there any disadvantage (performance issue) since the application creates more instances from MapperFunction class()?
My situation:
class Test {
private static void xxx(String s) throws SQLException {
System.out.println(s);
}
private static void yyy(Consumer<String> f) {
try {
f.apply('hello');
} catch (SQLException e) {
System.out.println("error");
}
}
public static void main(String args[])() {
yyy(xxx); // <-- not working!!
}
}
What I'm trying to do is to pass a static method as a parameter for another static method. I think that the correct way to declare the signature of the method yyy is with Consumer, but I'm not really sure about the other part, passing xxx as the parameter.
A possible solution I've found is to write
yyyy(s -> xxx(s));
But it looks ugly and it doesn't really work if xxx throws exceptions.
By using
yyy(Test::xxx);
I got this error
error: incompatible thrown types SQLException in method reference
You can use a method reference:
class Test {
private static void xxx(String s) {
//do something with string
}
private static void yyy(Consumer<String> c) {
c.accept("hello");
}
public static void zzz() {
yyy(Test::xxx);
}
}
You can try below code
class Test {
private static Consumer<String> xxx(String s) {
//do something with string
return null;// return Consumer for now passing null
}
private static void yyy(Consumer<String> f) {
//do something with Consumer
}
public static void zzz(){
yyy(xxx("hello"));
}
}
I'm not sure if this is possible with Java, but I'm trying to implement an interface that's unavailable at compile time** and pass it to another class as an object of that interface. Let's say I have an interface like:
public interface MyInterface {
void onReceive(int i);
}
and another class like:
public void MyClass {
ArrayList<MyInterface> listenerList = new ArrayList<MyInterface>();
public void add(MyInterface m) {
listenerList.add(m);
}
}
If they were available at compile time, I would be using them like:
blah = new MyInterface() {
public void onReceive(int i) {
System.out.println("BLAH");
}
}
MyClass mc = new MyClass();
myClass.add(blah);
I'm wondering if there is a way to write code that does the same as above if the first two classes are only available at runtime.
Thanks in advance!
**I'm trying to use a framework library from Android's ROM, but it is in dalvik bytecode so I can't use it for compilation.
UPDATE: Here's some sample code I used to test the solution:
File a/IIMSListener.java
// Excerpt from decompiled class
public interface IIMSListener
{
void onReceive(int p0, int p1/*, IMSParameter p2*/);
}
File a/IMSRemoteListenerStub.java
// Excerpt from decompiled class
import java.util.concurrent.*;
import java.util.*;
public class IMSRemoteListenerStub
{
public List<IIMSListener> mListenerList = new CopyOnWriteArrayList<IIMSListener>();
public boolean addListener(final IIMSListener iimsListener) {
if (iimsListener != null && !this.mListenerList.contains(iimsListener)) {
this.mListenerList.add(iimsListener);
return true;
}
return false;
}
public boolean removeListener(final IIMSListener iimsListener) {
if (iimsListener != null && this.mListenerList.contains(iimsListener)) {
this.mListenerList.remove(iimsListener);
return true;
}
return false;
}
}
File b/test.java
import java.lang.reflect.;
import java.util.;
public class test {
public static void main(String[] args) throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
NoSuchMethodException,
SecurityException,
ClassNotFoundException {
// Implement interface
Class<?> IIMSListener = Class.forName("IIMSListener");
Object listenerInstance = Proxy.newProxyInstance(IIMSListener.getClassLoader(), new Class<?>[]{IIMSListener}, new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("onReceive")){
System.out.println("ARGS: " + (Integer)args[0] + ", " + (Integer)args[1]);
return 1;
}
else return -1;
}
});
// Test
Method onReceive = listenerInstance.getClass().getDeclaredMethod("onReceive", new Class[] { int.class, int.class });
onReceive.invoke(listenerInstance, new Object[] { 1, 2 });
try {
// Pass to another class
Class IMSRemoteListenerStub = Class.forName("IMSRemoteListenerStub");
Constructor ctor = IMSRemoteListenerStub.getConstructor();
Object stubInstance = ctor.newInstance(new Object[] {});
Method addListener = stubInstance.getClass().getDeclaredMethod("addListener", new Class[] { IIMSListener });
addListener.invoke(stubInstance, new Object[] { listenerInstance });
// Test
Field mListenerList = IMSRemoteListenerStub.getField("mListenerList");
List<?> list = (List<?>)mListenerList.get(stubInstance);
onReceive.invoke(list.get(0), new Object[] { 3, 4 });
}
catch (InstantiationException e) {}
catch (NoSuchFieldException e) {}
}
}
Execution:
$ cd b
$ CLASSPATH=".:../a" java test
ARGS: 1, 2
ARGS: 3, 4
If it is going to be same interface then use Dynamic Proxies
//Loading the class at runtime
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
Class<?> someInterface = Class.forName("SomeInterface");
Object instance = Proxy.newProxyInstance(someInterface.getClassLoader(), new Class<?>[]{someInterface}, new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Handle the invocations
if(method.getName().equals("someMethod")){
return 1;
}
else return -1;
}
});
System.out.println(instance.getClass().getDeclaredMethod("someMethod", (Class<?>[])null).invoke(instance, new Object[]{}));
}