I have a few classes to test deep links. Each class test 1 deep link.
There are few tests that always terminate correctly at 10+ seconds when I run them individually but always take more than 45 seconds to finish when I run them together.
Note that all these tests are supposed to fail all the times now (I haven't done the implementation for them) so the results should not vary.
Am I using IdlingPolicies.setIdlingResourceTimeout wrong?
My test class:
#RunWith(AndroidJUnit4.class)
public class DeepLinkActivityBagTest extends MyActivityTest {
#Rule
public MyActivityTestRule<DeepLinkActivity> activityTestRule =
new MyActivityTestRule<DeepLinkActivity>(DeepLinkActivity.class) {
#Override
protected Intent getActivityIntent() {
Intent intent = super.getActivityIntent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("xxx://bag"));
return intent;
}
};
#Test
public void testCorrectScreenOpened() {
setUpWait();
String expectedToolbarTitle = "shopping bag" + mToolbarTitleSuffix;
onView(withTextIgnoreCase(expectedToolbarTitle)).check(matches(isDisplayed()));
}
}
Its parent class:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
#Rule
public MyActivityTestRule<DeepLinkActivity> activityTestRule;
protected String mToolbarTitleSuffix;
#Before
public void prepare() {
if (!BuildConfig.FLAVOR.equals(ENV_PRODUCTION)) {
mToolbarTitleSuffix = String.format(" (%s)", BuildConfig.FLAVOR);
}
}
#After
public void resetCountry() {
if (activityTestRule != null) {
PrefUtils.clear(InstrumentationRegistry.getTargetContext());
PrefUtils.setCurrentCountryCode(activityTestRule.getmCurCountryCode());
PrefUtils.setCurrentLangCode(activityTestRule.getmCurLangCode());
}
}
protected void setUpWait() {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
Logger.e(e, "");
}
IdlingPolicies.setMasterPolicyTimeout(10, TimeUnit.SECONDS);
IdlingPolicies.setIdlingResourceTimeout(5, TimeUnit.SECONDS);
}
}
MyActivityTestRule:
public class MyActivityTestRule<D extends Activity> extends ActivityTestRule {
private String mCurCountryCode;
private String mCurLangCode;
protected MyActivityTestRule(Class activityClass) {
super(activityClass);
}
public MyActivityTestRule(Class activityClass, boolean initialTouchMode) {
super(activityClass, initialTouchMode);
}
public MyActivityTestRule(Class activityClass, boolean initialTouchMode, boolean launchActivity) {
super(activityClass, initialTouchMode, launchActivity);
}
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
PrefUtils.setup(InstrumentationRegistry.getTargetContext());
mCurCountryCode = PrefUtils.getCurrentCountryCode();
mCurLangCode = PrefUtils.getCurrentLangCode();
PrefUtils.clear(InstrumentationRegistry.getTargetContext());
PrefUtils.setCurrentCountryCode("SG");
PrefUtils.setCurrentLangCode("en");
}
String getmCurCountryCode() {
return mCurCountryCode;
}
String getmCurLangCode() {
return mCurLangCode;
}
}
Related
I have written a RxDownloader class to download items from a predefined list. The downloader is working fine when exporting as debug aar(sdk) file but getting crashed in relase build with following stacktrace:-
java.lang.AbstractMethodError: abstract method "void io.reactivex.rxjava3.core.Observer.onSubscribe(io.reactivex.rxjava3.disposables.Disposable)"
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(ObservableSubscribeOn.java:34)
at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
at io.reactivex.rxjava3.core.Observable.subscribeWith(Observable.java:13229)
Following is RxDownloader class code:-
public class RxDownloader {
private static final String TAG = RxDownloader.class.getSimpleName();
private static Scheduler mScheduler;
public static void initRxDownloader() {
mScheduler = Schedulers.newThread();
}
public void downloadFromList( ArrayList<DownloadItemEntity> downloadItemEntities, IDownloadListener iDownloadListener) {
Observable downloadObservable = Observable.fromIterable(downloadItemEntities);
Observer downloadObserver = new Observer() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(Object o) {
DownloadItemEntity downloadItemEntity = (DownloadItemEntity) o;
downloadPictureFromURL(downloadItemEntity, iDownloadListener);
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
};
downloadObservable.subscribeOn(mScheduler).subscribeWith(downloadObserver);
}}
Anyone knows how to resolve this error?
From the start, sorry for bad English, I am working on it.
My goal is to create http methods in vert.x. Each method consists of steps, which can be blocked by other steps. For simplifying one step can be blocked by exactly one another.
I decided to create an AsyncMethodHandler which inside of handle method call, create exemplars of AsyncStepHandlers. Method handler also creates a map of steps futures, and try to create a compose handler for them to join.
here's the code AsyncMethodHandler:
public abstract class AsyncMethodHandler<T extends BaseChannelResponse> implements Handler<RoutingContext> {
private static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
private final List<Class<? extends AsyncStepHandler>> steplist;
private final HttpMethod methodType;
private final String endpointName;
private final HttpEndpointName endpoint;
private String responseEndpoint;
public AsyncMethodHandler(HttpEndpointName endpoint, String endpointName, HttpMethod methodType, List<Class<? extends AsyncStepHandler>> steplist) {
this.steplist = steplist;
this.endpoint = endpoint;
this.endpointName = endpointName;
this.methodType = methodType;
}
#Override
public void handle(RoutingContext event) {
try {
Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution = new ConcurrentHashMap<>(steplist.size());
List<AsyncStepHandler> handlers = new ArrayList<>(steplist.size());
for (Class<? extends AsyncStepHandler> stepClass : this.steplist) {
AsyncStepHandler stepHandler = stepClass.getConstructor(RoutingContext.class).newInstance(event);
mapOfExecution.put(stepClass, stepHandler.getStepFuture());
handlers.add(stepHandler);
}
for (AsyncStepHandler stepHandler : handlers) {
stepHandler.before(mapOfExecution).setHandler(stepHandler.makeHandler(mapOfExecution));
}
CompositeFuture.join(new ArrayList<>(mapOfExecution.values())).setHandler(handleResult(event, mapOfExecution));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
private Handler<AsyncResult<CompositeFuture>> handleResult(RoutingContext event, Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
return result -> {
if (result.succeeded()) {
succeeded(event.response(), generateResponse(mapOfExecution));
} else {
ChannelAPIException error = ChannelAPIException.createFrom(result.cause());
errored(event.response(), error.getCode(), error.getMessage());
}
};
}
protected abstract T generateResponse(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution);
private void errored(HttpServerResponse response, int code, String message) {
response.putHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON)
.setStatusCode(code)
.end(message);
CAPIMetricFactory.incBotResponseError(this.responseEndpoint, code);
}
private void succeeded(HttpServerResponse response, T result) {
response.putHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON)
.setStatusCode(200)
.end(Serializer.toPrettyJson(result));
CAPIMetricFactory.incBotResponse(this.responseEndpoint);
}
public String getEndpointName() {
return endpointName;
}
public HttpMethod getMethodType() {
return methodType;
}
public HttpEndpointName getEndpoint() {
return endpoint;
}
public void setResponseEndpoint(String responseEndpoint) {
this.responseEndpoint = responseEndpoint;
}
}
here's the code AsyncStepHandlers:
public abstract class AsyncStepHandler<T> {
private final Future stepFuture;
private final RoutingContext context;
private final Class<? extends AsyncStepHandler> before;
public AsyncStepHandler(RoutingContext context) {
this(Future.future(), context, null);
}
public AsyncStepHandler(RoutingContext context, Class<? extends AsyncStepHandler> before) {
this(Future.future(), context, before);
}
private AsyncStepHandler(Future stepFuture, RoutingContext context, Class<? extends AsyncStepHandler> before) {
this.stepFuture = stepFuture;
this.context = context;
this.before = before;
}
public static <T> T getResultFromMap(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution, Class<? extends AsyncStepHandler> key) {
return (T) mapOfExecution.get(key).result();
}
public final Future getStepFuture() {
return stepFuture;
}
public RoutingContext getContext() {
return context;
}
public Buffer getContextBody() {
return context.getBody();
}
public String getContextBodyAsString() {
return context.getBodyAsString();
}
public Future before(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
if (before != null) {
return mapOfExecution.get(before);
} else {
return Future.succeededFuture();
}
}
public abstract Future<T> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution);
public Handler<AsyncResult> makeHandler(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
return result -> {
if (result.succeeded()) {
this.execute(mapOfExecution).setHandler(this.finish());
} else {
stepFuture.fail(result.cause());
}
};
}
private Handler<AsyncResult<T>> finish() {
return result -> {
if (result.succeeded()) {
stepFuture.complete(result.result());
} else {
stepFuture.fail(result.cause());
}
};
}
}
So then, I try to create some actual methods and steps. For example:
create parameters object from the request body
from created earlier parameters get token and try to authorize
from an authorized object from the previous step consider validating the status of the request.
So here's the code:
public class SimpleTestMethod extends AsyncMethodHandler<TestData> {
public SimpleTestMethod(String endpoint) {
super(
CHANNEL_API_SEND_TEXT,
endpoint,
POST,
new ArrayList<Class<? extends AsyncStepHandler>>(){{
add(ParametersStep.class);
}{
add(AuthorizationStep.class);
}{
add(ValidateStep.class);
}}
);
}
#Override
protected TestData generateResponse(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("End");
SendMessageParameters response = (SendMessageParameters) mapOfExecution.get(ParametersStep.class).result();
ValidationResult validationResult = (ValidationResult) mapOfExecution.get(ValidateStep.class).result();
return new TestData(response.toString(),0l);
}
}
First if for example steps will be like this:
public class ParametersStep extends AsyncStepHandler<SendMessageParameters> {
public ParametersStep(RoutingContext context) {
super(context);
}
#Override
public Future<SendMessageParameters> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("ParametersStep");
SendMessageParameters parameters = parseJson(this.getContextBodyAsString(), SendMessageParameters.class);
return Future.succeededFuture(parameters);
}
}
Execution will be expectable. But if I will add some additional awaiting for some step, then the next after that step will never start.
For example:
public class AuthorizationStep extends AsyncStepHandler<AuthResponse> {
public AuthorizationStep(RoutingContext context) {
super(context, ParametersStep.class);
}
#Override
public Future<AuthResponse> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("AuthorizationStep");
final Future<AuthResponse> authorization = Future.future();
SendMessageParameters parameters = getResultFromMap(mapOfExecution, ParametersStep.class);
AuthResponse response = new AuthResponse(new ChannelTokenData(0l,parameters.getToken(),true,0l,0l,null));
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
authorization.complete(response);
}
});
t.start();
return authorization;
}
}
Then no steps, that awaits of authorization step ending will be called. I reading the official doc, and tried to find some information about this case, but did not succeed. I tried different technics setHandler, compose but get's the same result.
Can anybody help me with understanding why next step won't start and solving this issue because the next part is to use CompositeFuture =)
P.S.:
What is the most interesting, if for example AuthorizationStep is second in 3 steps method - execution will stop on second step. But if I do this:
#Override
public void handle(RoutingContext event) {
try {
Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution = new ConcurrentHashMap<>(steplist.size());
List<AsyncStepHandler> handlers = new ArrayList<>(steplist.size());
CountDownLatch latch = new CountDownLatch(steplist.size());
for (Class<? extends AsyncStepHandler> stepClass : this.steplist) {
AsyncStepHandler stepHandler = stepClass.getConstructor(RoutingContext.class).newInstance(event);
mapOfExecution.put(stepClass, stepHandler.getStepFuture());
handlers.add(stepHandler);
stepHandler.setLatch(latch);
}
for (AsyncStepHandler stepHandler : handlers) {
stepHandler.before(mapOfExecution).setHandler(stepHandler.makeHandler(mapOfExecution));
}
latch.await();
CompositeFuture.join(new ArrayList<>(mapOfExecution.values())).setHandler(handleResult(event, mapOfExecution));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
And this in AsyncStepHandler:
private Handler<AsyncResult<T>> finish() {
return result -> {
System.out.println("finish");
if (result.succeeded()) {
latch.countDown();
stepFuture.complete(result.result());
} else {
stepFuture.fail(result.cause());
}
};
}
Everything starts to work. If I add countdown latch, and add await before Composite future join, all will be fine.
I am new to Vertx.
I am playing with the API and I am trying to write a FileSizeHandler. I don't know if it is the correct way to do it but I would like to have your opinions.
In my code I would like to use the handler like this :
public class MyVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
getFileSize("./my_file.txt", event -> {
if(event.succeeded()){
Long result = event.result();
System.out.println("FileSize is " + result);
} else {
System.out.println(event.cause().getLocalizedMessage());
}
});
}
private void getFileSize(String filepath, Handler<AsyncResult<Long>> resultHandler){
resultHandler.handle(new FileSizeHandler(filepath));
}
}
Here is my FileSizeHandler class :
public class FileSizeHandler implements AsyncResult<Long> {
private boolean isSuccess;
private Throwable cause;
private Long result;
public FileSizeHandler(String filePath){
cause = null;
isSuccess = false;
result = 0L;
try {
result = Files.size(Paths.get(filePath));
isSuccess = !isSuccess;
} catch (IOException e) {
cause = e;
}
}
#Override
public Long result() {
return result;
}
#Override
public Throwable cause() {
return cause;
}
#Override
public boolean succeeded() {
return isSuccess;
}
#Override
public boolean failed() {
return !isSuccess;
}
}
What bothers me in the handler, is that I have to do it in the constructor of the class. Is there a better way to do it?
First of all, you called your class FileHandler, but it's not. It's a result.
You declare handler in Vert.x like that:
public class MyHandler implements Handler<AsyncResult<Long>> {
#Override
public void handle(AsyncResult<Long> event) {
// Do some async code here
}
}
Now, for what you do, there's vertx.fileSystem():
public class MyVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
vertx.fileSystem().readFile("./my_file.txt", (f) -> {
if (f.succeeded()) {
System.out.println(f.result().length());
}
else {
f.cause().printStackTrace();
}
});
}
}
I am completely new in junit testing, and I need to test a interactor with some async calls. My problem is that I do not understand what do I need to test without breaking the unity of the test.
If I test a pure call to interactor.execute(), I am testing other interactors functionalities too. I could mock interactor dependencies but they are inyected. I know it is a conceptual misunderstood, it must be much easier than I think.
public class NewLoginInteractorImpl implements NewLoginInteractor {
private final GetRegistrationIdInteractor getRegistrationIdInteractor;
private final GetDeviceIdInteractor getDeviceIdInteractor;
private final GetAndroidOSVersionInteractor getAndroidOSVersionInteractor;
private final NewLoginWebService newLoginWebService;
private static final String TAG = "NewLoginInteractorImpl";
#Inject
public NewLoginInteractorImpl(GetRegistrationIdInteractor getRegistrationIdInteractor, GetDeviceIdInteractor getDeviceIdInteractor, GetAndroidOSVersionInteractor getAndroidOSVersionInteractor, NewLoginWebService newLoginWebService) {
this.getRegistrationIdInteractor = getRegistrationIdInteractor;
this.getDeviceIdInteractor = getDeviceIdInteractor;
this.getAndroidOSVersionInteractor = getAndroidOSVersionInteractor;
this.newLoginWebService = newLoginWebService;
}
#Override
public void execute(final Callback callback) {
final String deviceId = getDeviceIdInteractor.execute();
final String os_version = getAndroidOSVersionInteractor.execute();
getRegistrationIdInteractor.execute(new GetRegistrationIdInteractor.Callback() {
#Override
public void onSuccess(String regid) {
NoLoginUser noLoginUser = new NoLoginUser(deviceId, regid, os_version);
callNoLoginWebService(noLoginUser, callback);
}
#Override
public void onFailure(String error) {
callback.onFailureGettingRegistrationId(error);
}
#Override
public void onRecoverableError(int resultCode, int playServicesResolutionRequest) {
callback.onRecoverableError(resultCode, playServicesResolutionRequest);
}
});
}
private void callNoLoginWebService(NoLoginUser noLoginUser, final Callback callback) {
newLoginWebService.noLogin(noLoginUser, new NewLoginWebService.Callback() {
#Override
public void onNoLoginFailure(String errorText) {
callback.onNoLoginFailure(errorText);
}
#Override
public void onNoLoginInvalidValues(JSONObject error) {
callback.onFailureGettingRegistrationId("ERROR");
}
#Override
public void onNoLoginSuccess(JSONObject response) {
callback.onSuccess(response);
}
#Override
public void onNullResponse() {
callback.onNullResponse();
}
});
}
}
I'm reading "Design Pattern for Dummies". I read and practiced Decorator Pattern. With Decorator Pattern, we can decorate an object with anything. Now, I want to remove decorated object before decorated.I have solved this problem by an ArrayList but I still feel it's not good. Can you tell me how to remove a decorated object? And what is a better way?
this is my way:
Computer.java
public class Computer {
public Computer() {
}
public String description() {
return "computer";
}
}
ComponentDecorator.java
public abstract class ComponentDecorator extends Computer {
#Override
public abstract String description();
}
CD.java
public class CD extends ComponentDecorator {
private Computer computer;
public CD() {
}
public CD(Computer computer) {
this.computer = computer;
}
#Override
public String description() {
return computer.description() + " and a CD";
}
}
Disk.java
public class Disk extends ComponentDecorator {
private Computer computer;
public Disk() {
}
public Disk(Computer c) {
computer = c;
}
#Override
public String description() {
return computer.description() + " and a disk";
}
}
Monitor.java
public class Monitor extends ComponentDecorator {
private Computer computer;
public Monitor() {
}
public Monitor(Computer computer) {
this.computer = computer;
}
#Override
public String description() {
return computer.description() + " and a monitor";
}
}
Main.java
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
static ArrayList<ComponentDecorator> list = new ArrayList<>();
public static void main(String[] args) {
addComponent(new CD(), new Disk(), new Monitor());
System.out.println(list.size());
Computer penIII = getComputer();
removeComponent(new Monitor());
penIII = getComputer();
System.out.println(penIII.description());
}
private static void addComponent(ComponentDecorator... comp) {
list.addAll(Arrays.asList(comp));
}
private static void removeComponent(ComponentDecorator comp) {
for(ComponentDecorator c : list) {
if(c.getClass() == comp.getClass()) {
list.remove(list.indexOf(c));
break;
}
}
}
private static Computer getComputer() {
Computer c = new Computer();
Class e;
for(ComponentDecorator d : list) {
e = d.getClass();
try {
c = (Computer) e.getConstructor(new Class[]{Computer.class}).newInstance(c);
} catch(Exception ex) {
ex.printStackTrace();
}
}
return c;
}
}
A nicer way would be adding the "removeDecorator" method to your ComponentDecorator class.
public abstract class ComponentDecorator {
private ComponentDecorator subject;
public ComponentDecorator(ComponentDecorator subject) {
this.subject = subject;
}
#Override
public abstract String description();
}
public void removeDecorator(ComponentDecorator toRemove) {
if (subject == null) {
return;
} else if (subject.equals(toRemove)) {
subject = subject.getSubject();
} else {
subject.removeDecorator(toRemove);
}
}
public ComponentDecorator getSubject() {
return subject;
}
// Computer
public class Computer extends ComponentDecorator{
public Computer() {
super(null);
}
public String description() {
return "computer";
}
// CD
public class CD extends ComponentDecorator {
public CD(ComponentDecorator computer) {
super(computer);
}
#Override
public String description() {
return getSubject().description() + " and a CD";
}
}
// main
public static void main(String[] args) {
ComponentDecorator penIII = new Computer();
penIII = new CD(penIII);
penIII = new Monitor(penIII);
System.out.println(penIII.description());
}
}
If you don't have the reference of the decorator to remove, you can create another method that the a Class instead.
You'll need to the decorated object as "ComponentDecorator" instead of "Computer" however. I suggest to make the Computer class extends ComponentDecorator instead of the other way around.
I suspect I'm misunderstanding your question, but to get the decorated (inner) object out of the decorator, you can just add a get method to the decorators. Add
public abstract Computer getDecorated();
to ComponentDecorator and
public Computer getDecorated(){return computer;}
to each subclass (CD, Monitor, ...). Is that what you were looking for?
Add two methods to an interface, undecorate() and removeDecoration(String className):
ThingInterface.java
public interface ThingInterface {
public ThingInterface undecorate();
public ThingInterface removeDecoration(String className);
public String nonDecoratedString();
public String decoratedString();
}
Your base class will simply return itself for those methods:
BaseThing.java
public class BaseThing implements ThingInterface {
private String basicString;
public BaseThing(String string) {
basicString = string;
}
#Override
public ThingInterface undecorate() {
return this;
}
#Override
public ThingInterface removeDecoration(String className) {
return this;
}
#Override
public String nonDecoratedString() {
return basicString;
}
#Override
public String decoratedString() {
return basicString;
}
}
Now the real meat of what you need is in the abstract class:
AbstractThingDecorator.java
public abstract class AbstractThingDecorator implements ThingInterface {
private ThingInterface thing;
public AbstractThingDecorator(ThingInterface thing) {
this.thing = thing;
}
#Override
public ThingInterface removeDecoration(String className) {
ThingInterface undecorate = this;
if(this.getClass().getName() == className) {
undecorate = this.undecorate();
}
else {
ArrayList<String> classStack = new ArrayList();
while(undecorate != undecorate.undecorate()) {
if(undecorate.getClass().getName() != className) {
classStack.add(undecorate.getClass().getName());
}
undecorate = undecorate.undecorate();
}
for(int i = classStack.size()-1;i == 0;i--) {
try {
Class<?> clazz = Class.forName(classStack.get(i));
Constructor<?> ctor = clazz.getConstructor(ThingInterface.class);
Object object = ctor.newInstance(new Object[] { undecorate });
undecorate = (ThingInterface) object;
}
catch(Exception e) {
System.out.println("Exception:" + e.getMessage());
}
}
}
return undecorate;
}
#Override
public ThingInterface undecorate() {
return this.thing;
}
#Override
public String nonDecoratedString() {
return thing.nonDecoratedString();
}
#Override
public String decoratedString() {
return thing.decoratedString();
}
}
I'm adding two simple decorators, ThingDecorator and FancyThingDecorator:
ThingDecorator.java
public class ThingDecorator extends AbstractThingDecorator {
public ThingDecorator(ThingInterface thing) {
super(thing);
}
#Override
public ThingInterface undecorate() {
return super.undecorate();
}
#Override
public String decoratedString() {
return super.decoratedString() + ", decorated";
}
}
FancyThingDecorator.java
public class FancyThingDecorator extends AbstractThingDecorator {
public FancyThingDecorator(ThingInterface thing) {
super(thing);
}
#Override
public ThingInterface undecorate() {
return super.undecorate();
}
#Override
public String decoratedString() {
return super.decoratedString() + ", fancy";
}
}
Finally, my java main:
Decorator.java
public class Decorator {
/**
* #param args
*/
public static void main(String[] args) {
ThingInterface thing = new BaseThing("Basic string");
ThingInterface decorator = new ThingDecorator(thing);
ThingInterface fancyDecorator = new FancyThingDecorator(thing);
ThingInterface extraFancy = new FancyThingDecorator(new ThingDecorator(thing));
ThingInterface undecorate = new FancyThingDecorator(new ThingDecorator(thing));
System.out.println("Basic thing is: " + thing.decoratedString()+".");
System.out.println("Decorated thing is: " + decorator.decoratedString()+".");
System.out.println("Fancy thing is: " + fancyDecorator.decoratedString()+".");
System.out.println("Decorated fancy thing is: " + extraFancy.decoratedString()+".");
while(extraFancy.undecorate() != extraFancy) {
extraFancy = extraFancy.undecorate();
System.out.println("Rolling back decorations: " + extraFancy.decoratedString()+".");
}
System.out.println("Decoration chain before removal is: " + undecorate.decoratedString());
System.out.println("Removing decoration for " + ThingDecorator.class.getName());
undecorate = undecorate.removeDecoration(ThingDecorator.class.getName());
System.out.println("Decoration chain after removal is: " + undecorate.decoratedString()+".");
}
}
The output is:
Basic thing is: Basic string.
Decorated thing is: Basic string, decorated.
Fancy thing is: Basic string, fancy.
Decorated fancy thing is: Basic string, decorated, fancy.
Rolling back decorations: Basic string, decorated.
Rolling back decorations: Basic string.
Decoration chain before removal is: Basic string, decorated, fancy
Removing decoration for ThingDecorator
Decoration chain after removal is: Basic string, fancy.