I am working on CS50 Android track Fiftygram app. My code is working but I don't like to see copy pasted codes.
Currently checking dropdown selected item name and using if/else if to call with representing instance of a Transformation. How can I directly call apply function with the string without using all these if and else ifs.
If I can find a way, I can fix some of the wording. For example, I can get rid of Transformation end of the strings and add it myself before calling the function.
public void applyFilter(View view) {
if (filterList.getSelectedItem().toString() != null) {
if (filterList.getSelectedItem().toString().equals("ToonFilterTransformation")) {
apply(new ToonFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("SepiaFilterTransformation")) {
apply(new SepiaFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("ContrastFilterTransformation")) {
apply(new ContrastFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("InvertFilterTransformation")) {
apply(new InvertFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("PixelationFilterTransformation")) {
apply(new PixelationFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("SketchFilterTransformation")) {
apply(new SketchFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("SwirlFilterTransformation")) {
apply(new SwirlFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("KuwaharaFilterTransformation")) {
apply(new KuwaharaFilterTransformation());
}
else if (filterList.getSelectedItem().toString().equals("VignetteFilterTransformation")) {
apply(new VignetteFilterTransformation());
}
}
}
public void apply(Transformation<Bitmap> filter) {
if (original != null) {
Glide
.with(this)
.load(original)
.apply(RequestOptions.bitmapTransform(filter))
.into(imageView);
}
}
I suggest you to create a method and make filterList.getSelectedItem().toString() into a variable. Then make use of reflection in creating a class
something like
String item = filterList.getSelectedItem().toString();
apply(createInstance(item))
public Object createInstance(String item){
Class classDefinition = Class.forName(item);
return classDefinition.newInstance();
}
But you have to add further validations there if the item exists or not.
But the idea is like that. Take advantage of reflection instead of using new keyword
Related
I am trying to refactor old SimpleFormController. I would like to replace getSuccessView() and gerFormView() calls with actual success view and form view Strings.
I went through https://spoon.gforge.inria.fr/first_transformation.html, it shows how to generate and add statements however I could not understand how to modify.
I have tried couple of things.
Replace statements with the getSuccessView() and getFormView() calls
public class SimpleFormControllerReplaceViewCall extends AbstractProcessor<CtMethod> {
MetaData meta;
String successView= "successView";
String formView = "formView";
public SimpleFormControllerReplaceViewCall(MetaData meta) {
this.meta = meta;
}
#Override
public boolean isToBeProcessed(CtMethod candidate) {
if(candidate.getBody() == null) { //Ignore abstract methods
return false;
}
String sourceCode;
try {
sourceCode = candidate.getBody()
.getOriginalSourceFragment()
.getSourceCode();
} catch (Exception e) {
return false;
}
return sourceCode.contains(getViewFunctionName(successView))
|| sourceCode.contains(getViewFunctionName(formView));
}
#Override
public void process(CtMethod method) {
Node beanNode = getBeanNode(method);
CtBlock<Object> body = getFactory().createBlock();
method.getBody().getStatements()
.stream()
.map(s -> {
Optional<String> sourceCode = getStatementSourceCode(s);
if(!sourceCode.isPresent()) {
return s.clone(); // Clone required to handle runtime error for trying attach a node to two parents
} else {
System.out.println("Modifying: " + method.getSignature());
String code = sourceCode.get();
code = replaceViewCalls(beanNode, code, successView);
code = replaceViewCalls(beanNode, code, formView);
return getFactory().createCodeSnippetStatement(code);
}
}).forEach(body::addStatement);
method.setBody(body);
}
private Optional<String> getStatementSourceCode(CtStatement s) {
String sourceCode = null;
try {
sourceCode = s.getOriginalSourceFragment()
.getSourceCode();
} catch (Exception e) {}
System.out.println(sourceCode);
if (sourceCode != null &&
(sourceCode.contains(getViewFunctionName(successView))
|| sourceCode.contains(getViewFunctionName(formView)))) {
sourceCode = sourceCode.trim();
if(sourceCode.endsWith(";"))
sourceCode = sourceCode.substring(0, sourceCode.length()-1);
return Optional.of(sourceCode);
} else {
return Optional.empty();
}
}
public String replaceViewCalls(Node beanNode, String code, String viewType) {
String getViewFunctionName = getViewFunctionName(viewType);
if (!code.contains(getViewFunctionName)) {
return code;
}
String view = AppUtil.getSpringBeanPropertyValue(beanNode, viewType);
return code.replaceAll(getViewFunctionName + "\\(\\)", String.format("\"%s\"", view));
}
public Node getBeanNode(CtMethod method) {
String qualifiedName = method.getParent(CtClass.class).getQualifiedName();
return meta.getFullyQualifiedNameToNodeMap().get(qualifiedName);
}
private String getViewFunctionName(String viewType) {
return "get" + viewType.substring(0, 1).toUpperCase() + viewType.substring(1);
}
}
This however adds unwanted at end of blocks if() {... }; This creates syntax errors when if {} else {} blocks contain return statement(s). Auto import is turned on and imports are not added when there is more one class with same name (e.g., Map is present in classpath from few libraries) - this is consistent with the document. Can this be avoided when refactoring code? Original java file has correct imports.
Another approach I tried is to directly manipulate the body as a whole.
#Override
public void process(CtMethod method) {
String code = method.getBody()
.getOriginalSourceFragment()
.getSourceCode();
Node beanNode = getBeanNode(method);
code = replaceViewCalls(beanNode, code, successView);
code = replaceViewCalls(beanNode, code, formView);
CtCodeSnippetStatement codeStatement = getFactory().createCodeSnippetStatement(code);
method.setBody(codeStatement);
}
this still has same auto import issue as first one. Apart from that it adds redundant curly braces, for examples
void method() { x=y;}
will become
void method() { {x=y;} }
That that will be pretty printed ofcourse.
Also javadocs for getOriginalSourceFragment() also has below warning
Warning: this is a advanced method which cannot be considered as part
of the stable API
One more thing I thought of doing is creating pattern for each type of usage of getSuccessView() like
viewName = getSuccessView();
return getSuccessView();
return ModelAndView(getSuccessView(), map); etc, however for that I will have to write a whole bunch of processors / templates.
Since it is simple replacement, easiest is do something like below
//Walk over all files and execute
Files.lines(Paths.get("/path/to/java/file"))
.map(l -> l.replaceAll("getSuccessView\\(\\)", "actualViewNameWithEscapedQuotes"))
.map(l -> l.replaceAll("getFormView\\(\\)", "actualViewNameWithEscapedQuotes"))
.forEach(l -> {
//write to file
});
Since I can avoid text manipulation with the help of spoon for things like changing modifiers, annotations, method name, annotations etc, I am hoping there should be a better way to modify the method body.
You should treat the processor input as an abstract syntax tree instead of a string:
public class SimpleFormControllerReplaceViewCall extends AbstractProcessor<CtMethod<?>> {
#Override
public boolean isToBeProcessed(CtMethod candidate) {
if(candidate.isAbstract()) { //Ignore abstract methods
return false;
}
return !candidate.filterChildren((CtInvocation i)->
i.getExecutable().getSimpleName().equals("getSuccessView")
|| i.getExecutable().getSimpleName().equals("getFormView")).list().isEmpty();
}
#Override
public void process(CtMethod<?> ctMethod) {
Launcher launcher = new Launcher();
CodeFactory factory = launcher.createFactory().Code();
List<CtInvocation> invocations = ctMethod.filterChildren((CtInvocation i)->
i.getExecutable().getSimpleName().equals("getSuccessView")
|| i.getExecutable().getSimpleName().equals("getFormView")).list();
for(CtInvocation i : invocations) {
if(i.getExecutable().getSimpleName().equals("getSuccessView")) {
i.replace(factory.createLiteral("successView"));
} else {
i.replace(factory.createLiteral("formView"));
}
}
}
}
Here the CtMethod AST is traversed in search for CtInvocation elements with the specified properties. The found elements are then replaced with new string literal elements.
I am newbie in Java. How to create function to get one object or null?
I have simple class:
public class Auto {
Auto (String text) {
}
}
And other class I would like to have method onAuto:
public class AutoSearch {
public Auto oneAuto()
{
//operations
String text = getOperations();
if (text) {
Auto auto = new Auto(text);
return auto;
} else {
return null;
}
}
}
But this not working, because method have to return object Auto.
What if I can't create object Auto in this method? How can I do it?
You want to verify if text exists in order to create an Auto object, am I correct?
public class AutoSearch {
public Auto oneAuto() {
String text = getOperations();
if (text != null) {
return new Auto(text);
} else {
return null;
}
}
}
You can do this in a more compact way too, using the ternary operator:
public class AutoSearch {
public Auto oneAuto() {
return text != null ? new Auto(text) : null;
}
}
Yet, in Java the expression inside an if statement MUST resolve to an boolean (true or false). You seems to think that Java works like Javascript, and its definitely not the case. Java and Javascript have only the name in common.
As davidxxx said, probably you will need to define the Auto constructor as public.
1) if (text) is not a boolean expression.
It cannot compile. But if (text != null) can.
2)You declared the Auto constructor with a package private access modifier.
It means that AutoSearch has to be in the same package as Auto to be able to invoke this constructor.
Or else change the Auto constructor to public :
public Auto (String text) {
....
}
You need to check if the text is empty or not. if it is empty return null and if it have content, then return the Object
if("".equals(text) || text == null){
return null;
} else {
return auto
}
Clean code means for me: only one task for each methode and no nested loops.
When I got the following code, I asked myself, how can I avoid nested for loops and encapsulate them in methods.
private String getUser(){
for (FieldConfigScheme context : getConfigurationSchemes()) {
for (Option option : getOptions(context)) {
for (Group group : getGroups()) {
if (option.getValue().equalsIgnoreCase(group.getName())) {
return group.getUser();
}
}
}
}
return "default";
}
My first solution was the following. The problem here is, the for loops are running until the end and do not break (return) when the value is found and set.
private String user = "default";
private String getUser(){
for (FieldConfigScheme context : getConfigurationSchemes()) {
processOptions(context);
}
return this.user;
}
private void processOptions(FieldConfigScheme context){
for (Option option : getOptions(context)) {
processGroups(option);
}
}
private void processGroups(Option option){
for (Group group : getGroups()) {
setUser(option, group);
}
}
private void setUser(Option option, Group group){
if (option.getValue().equalsIgnoreCase(group.getName())) {
this.user = group.getUser();
}
}
so I wrote this code, which should be the same like the first:
private String user = "default";
private boolean isUserSet = false;
private String getUser(){
for (FieldConfigScheme context : getConfigurationSchemes()) {
if(!isUserSet) processOptions(context);
else return this.user;
}
return this.user;
}
private void processOptions(FieldConfigScheme context){
for (Option option : getOptions(context)) {
if(!isUserSet) processGroups(option);
else return;
}
}
private void processGroups(Option option){
for (Group group : getGroups()) {
if(!isUserSet) setUser(option, group);
else return;
}
}
private void setUser(Option option, Group group){
if (option.getValue().equalsIgnoreCase(group.getName())) {
this.user = group.getUser();
isUserSet = true;
}
}
But then I asked myself, is this really better code? Is this more clean code? Yes, every method is only doing one thing. And yes, the code is better to read in my opinion. But from originally 12 lines compact code I now got 30 lines of code and one member variable more in the code. So is the first originally code better because it's more compact even with nested for loops?
What do you think? Which one is better? Or how can I write the code better?
Thanks in advance for your answers!
Instead of returning void, why not boolean?
private String getUser(){
for (FieldConfigScheme context : getConfigurationSchemes()) {
if (processOptions(context)) {
break;
}
}
return this.user;
}
private boolean processOptions(FieldConfigScheme context){
for (Option option : getOptions(context)) {
if (processGroups(option)) {
return true;
}
}
return false;
}
private boolean processGroups(Option option){
for (Group group : getGroups()) {
if (option.getValue().equalsIgnoreCase(group.getName())) {
this.user = group.getUser();
return true;
}
}
return false;
}
T.B.H. I prefer the nested loops method. It looks clean, there is nothing more going on in the loop than to simply find something in a hierarchy and this is perfectly fine.
The use of extra function in this case is just bad. Imagine having to debug this code now, rather than focusing on one method which is doing this, you will have to look at all the extra ones you made.
Also this method doesn't seem to take any parameters which suggests that it actually only needs to do this check once and the rest of the time it should just return the same value. That just a guess, but if that was the case, then it makes your efforts to make it cleaner all the more unnecessary.
Like lots of askers on SO, I'm relatively new to java and have attempted to teach myself android programming with some decent success. I'm sure this is trivial to someone with actual knowledge on the subject. I'm working on a app that attempts to fetch data from the net and 'returns true' if you get the data and 'returns false' if it doesn't. I want to do something when it returns false but can't figure out how to properly handle the response. Right now, I just ignore the response an do nothing. Any help?
public void onBackPressed() {
Someclass.getinfo().maybeShowInfo(this);
finish();
}
What I would like to do is something like (in pseudo code)
public void onBackPressed() {
Someclass.getinfo().maybeShowInfo(this);
// if false is returned
// do something
// else
// finish();
}
public void onBackPressed() {
boolean result = Someclass.getinfo().maybeShowInfo(this);
if (result) {
finish();
} else {
// do something else
}
}
It looks to me like you've combined two things that must be separate. Make fetching the data and displaying two methods, by two classes.
private InfoDao infoDao; // This is a class that gets the data; it's a member of the class with the onBackPressed() method
public void onBackPressed() {
Info info = this.infoDao.find();
if (info != null) {
displayInfo();
}
}
public void onBackPressed()
{
boolean result = Someclass.getinfo().maybeShowInfo(this);
if (result = false)
{
//do work for false response;
}
else
{
finish();
}
}
don't forget that you have to make your Someclass.getinfo() return true if it succeded and false if it didn't.
I currently have code to share a variable between two entry points in my application. The variable is the iconCount variable used to indicate how many notices the user has which is displayed on the home screen beside the icon. The way I've managed to do this is with a singleton and it (seems) to work fine at the moment. The issue is now that I do not want those notices to reset to zero when I completely turn off and turn on the phone. Should there be 7 notifications, I want there to be 7 notifications even after a device restart. For this I apparently need a persistent store integration which I've researched for a while.
So far my code for the bare singleton is:
public class MyAppIndicator{
public ApplicationIndicator _indicator;
public static MyAppIndicator _instance;
MyAppIndicator () {
setupIndicator();
}
public static MyAppIndicator getInstance() {
if (_instance == null) {
_instance = new MyAppIndicator ();
}
return(_instance);
}
public void setupIndicator() {
//Setup notification
if (_indicator == null) {
ApplicationIndicatorRegistry reg = ApplicationIndicatorRegistry.getInstance();
_indicator = reg.getApplicationIndicator();
if(_indicator == null) {
ApplicationIcon icon = new ApplicationIcon(EncodedImage.getEncodedImageResource ("notificationsdemo_jde.png"));
_indicator = reg.register(icon, false, true);
_indicator.setValue(0);
_indicator.setVisible(false);
}
}
}
public void setVisible1(boolean visible, int count) {
if (_indicator != null) {
if (visible) {
_indicator.setVisible(true);
_indicator.setValue(count); //UserInterface.incrementCount()
} else {
_indicator.setVisible(false);
}
}
}
}
I have been using the blackberry tutorial to figure out how to implement the persistable storage: http://supportforums.blackberry.com/t5/Java-Development/Storing-persistent-data/ta-p/442747
Now before I go any further I must stress I'm very new to java development so my coding might be completely wrong, but here is what I've tried to do:
public void setVisible1(boolean visible, int count) {
if (_indicator != null) {
if (visible) {
_indicator.setVisible(true);
_indicator.setValue(count); //UserInterface.incrementCount()
StoreInfo info = new StoreInfo();
info.incElement();
synchronized (persistentCount) {
//persistentCount.setContents(_data);
persistentCount.commit();
}
} else {
_indicator.setVisible(false);
}
}
}
static {
persistentCount = PersistentStore.getPersistentObject(0xdec6a67096f833cL);
synchronized (persistentCount) {
if (persistentCount.getContents() == null) {
persistentCount.setContents(new Vector()); //don't know what to do with this?
persistentCount.commit();
}
}
}
private static final class StoreInfo implements Persistable{
private int iconCount;
public StoreInfo(){}
public int getElement(){
return (int)iconCount;
}
public void incElement(){
iconCount++; //persistently increment icon variable
}
public void resetElement(){
iconCount=0; //when user checks application
}
}
The code above doesn't work which I'd expect somehow because I'm having trouble implementing the persistent portion. If anyone has any idea or input on how to accomplish this any assistance would be helpful. And of course thanks in advance.
In the example they have a variable called _data that holds the StoreInfo class, so first of all you should be keeping the StoreInfo in some variable. To do this have something like the following in your static initializer:
persistentCount = PersistentStore.getPersistentObject(0xdec6a67096f833cL);
synchronized (persistentCount) {
if (persistentCount.getContents() == null) {
persistentCount.setContents(new StoreInfo());
persistentCount.commit();
}
}
_data = (StoreInfo)persistentCount.getContents();
Now when you want to update it and save to the PersistentStore you can have something like:
_data.incElement();
synchronized(persistentCount) {
persistentCount.setContents(_data);
persistentCount.commit();
}
Assuming you're going to only ever have one instance of StoreInfo it could be better to put the commit code into the modifier methods so you don't forget to save the new values to the PersistentStore.