Updated Branches: refs/heads/sandbox/optional [created] 676bd3863
initial Optional impl Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/676bd386 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/676bd386 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/676bd386 Branch: refs/heads/sandbox/optional Commit: 676bd3863c5781703102b524d440d0145bb76a2a Parents: d007c13 Author: Igor Vaynberg <[email protected]> Authored: Mon Jan 30 22:48:57 2012 -0800 Committer: Igor Vaynberg <[email protected]> Committed: Mon Jan 30 22:48:57 2012 -0800 ---------------------------------------------------------------------- .../wicket/ajax/markup/html/AjaxFallbackLink.java | 17 +- .../org/apache/wicket/markup/html/form/Check.java | 2 +- .../wicket/markup/html/form/FormComponent.java | 5 +- .../org/apache/wicket/markup/html/form/Radio.java | 3 +- .../apache/wicket/markup/html/tree/BaseTree.java | 9 +- .../java/org/apache/wicket/MockPanelWithLink.java | 10 +- .../wicket/ajax/AjaxHeaderContributionPage.java | 13 +- .../wicket/ajax/AjaxHeaderContributionPage2.java | 19 +- .../org/apache/wicket/ajax/DomReadyOrderPage.java | 5 +- .../util/tester/apps_5/AjaxLinkClickTest.java | 9 +- .../apache/wicket/util/tester/apps_6/LinkPage.java | 5 +- .../java/org/apache/wicket/util/lang/Optional.java | 256 +++++++++++++++ 12 files changed, 311 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java index fe78ecb..d1af957 100644 --- a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java +++ b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java @@ -24,6 +24,7 @@ import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.model.IModel; +import org.apache.wicket.util.lang.Optional; /** * An ajax link that will degrade to a normal request if ajax is not available or javascript is @@ -139,16 +140,22 @@ public abstract class AjaxFallbackLink<T> extends Link<T> implements IAjaxLink @Override public final void onClick() { - onClick(null); + onClick((AjaxRequestTarget)null); + } + + @Override + public void onClick(final AjaxRequestTarget target) + { + onClick(Optional.of(target)); } /** * Callback for the onClick event. If ajax failed and this event was generated via a normal link - * the target argument will be null + * the target optional will contain a null * * @param target - * ajax target if this linked was invoked using ajax, null otherwise + * ajax request target */ - @Override - public abstract void onClick(final AjaxRequestTarget target); + public abstract void onClick(final Optional<AjaxRequestTarget> target); + } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Check.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Check.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Check.java index 85dfb8c..3746cab 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Check.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Check.java @@ -176,7 +176,7 @@ public class Check<T> extends LabeledWebMarkupContainer if (group.hasRawInput()) { - final String raw = group.getRawInput(); + final String raw = group.getRawInput().get(null); if (!Strings.isEmpty(raw)) { final String[] values = raw.split(FormComponent.VALUE_SEPARATOR); http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java index ec7b2f9..96aa942 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/FormComponent.java @@ -42,6 +42,7 @@ import org.apache.wicket.model.Model; import org.apache.wicket.util.convert.ConversionException; import org.apache.wicket.util.convert.IConverter; import org.apache.wicket.util.lang.Args; +import org.apache.wicket.util.lang.Optional; import org.apache.wicket.util.lang.WicketObjects; import org.apache.wicket.util.string.StringList; import org.apache.wicket.util.string.StringValue; @@ -781,9 +782,9 @@ public abstract class FormComponent<T> extends LabeledWebMarkupContainer * * @return The raw form input that is stored for this formcomponent */ - public final String getRawInput() + public final Optional<String> getRawInput() { - return NO_RAW_INPUT.equals(rawInput) ? null : rawInput; + return NO_RAW_INPUT.equals(rawInput) ? Optional.<String> ofNull() : Optional.of(rawInput); } /** http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Radio.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Radio.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Radio.java index e4d0cab..7ec502d 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Radio.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Radio.java @@ -169,8 +169,7 @@ public class Radio<T> extends LabeledWebMarkupContainer // checked attribute, first check if there was a raw input on the group. if (group.hasRawInput()) { - String rawInput = group.getRawInput(); - if (rawInput != null && rawInput.equals(value)) + if (group.getRawInput().isValueEqualTo(value)) { tag.put("checked", "checked"); } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/BaseTree.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/BaseTree.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/BaseTree.java index 4c6dd29..63f3643 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/BaseTree.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/BaseTree.java @@ -35,6 +35,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.request.Response; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.request.resource.ResourceReference; +import org.apache.wicket.util.lang.Optional; import org.apache.wicket.util.string.Strings; /** @@ -415,13 +416,11 @@ public abstract class BaseTree extends AbstractTree { private static final long serialVersionUID = 1L; - /** - * @see org.apache.wicket.ajax.markup.html.AjaxFallbackLink#onClick(org.apache.wicket.ajax.AjaxRequestTarget) - */ @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { - callback.onClick(target); + // FIXME OPTIONAL should not use getUnsafe() + callback.onClick(target.getUnsafe()); } }; } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/MockPanelWithLink.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/MockPanelWithLink.java b/wicket-core/src/test/java/org/apache/wicket/MockPanelWithLink.java index ce20aca..1a3d69a 100644 --- a/wicket-core/src/test/java/org/apache/wicket/MockPanelWithLink.java +++ b/wicket-core/src/test/java/org/apache/wicket/MockPanelWithLink.java @@ -21,6 +21,7 @@ import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.IMarkupCacheKeyProvider; import org.apache.wicket.markup.IMarkupResourceStreamProvider; import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.util.lang.Optional; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.StringResourceStream; @@ -46,9 +47,9 @@ public abstract class MockPanelWithLink extends Panel add(new AjaxFallbackLink<Void>("link") { @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { - MockPanelWithLink.this.onLinkClick(target); + MockPanelWithLink.this.onLinkClick(target.getUnsafe()); } }); } @@ -57,8 +58,9 @@ public abstract class MockPanelWithLink extends Panel * The callback to execute when the link is clicked. * * @param target - * the current Ajax request target. May be {@code null} if - * {@link org.apache.wicket.util.tester.BaseWicketTester#clickLink(String, boolean false)} is used. + * the current Ajax request target. May be {@code null} if {@link + * org.apache.wicket.util.tester.BaseWicketTester#clickLink(String, boolean false)} + * is used. */ protected abstract void onLinkClick(AjaxRequestTarget target); http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage.java b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage.java index a7ccde9..ca6c6f0 100644 --- a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage.java +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage.java @@ -19,6 +19,7 @@ package org.apache.wicket.ajax; import org.apache.wicket.Component; import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.util.lang.Optional; /** * @author jcompagner @@ -43,13 +44,13 @@ public class AjaxHeaderContributionPage extends WebPage private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { - target.prependJavaScript("prepend();"); - target.add(test1); - target.add(test2); - target.add(test3); - target.appendJavaScript("append();"); + target.get().prependJavaScript("prepend();"); + target.get().add(test1); + target.get().add(test2); + target.get().add(test3); + target.get().appendJavaScript("append();"); } }); } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage2.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage2.java b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage2.java index 44c6d4f..327b9ea 100644 --- a/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage2.java +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/AjaxHeaderContributionPage2.java @@ -19,6 +19,7 @@ package org.apache.wicket.ajax; import org.apache.wicket.Component; import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.util.lang.Optional; /** * @author jcompagner @@ -50,16 +51,16 @@ public class AjaxHeaderContributionPage2 extends WebPage private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { - target.prependJavaScript("prepend();"); - target.add(test1); - target.add(test2); - target.add(test3); - target.add(btest1); - target.add(btest2); - target.add(btest3); - target.appendJavaScript("append();"); + target.get().prependJavaScript("prepend();"); + target.get().add(test1); + target.get().add(test2); + target.get().add(test3); + target.get().add(btest1); + target.get().add(btest2); + target.get().add(btest3); + target.get().appendJavaScript("append();"); } }); } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/ajax/DomReadyOrderPage.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/DomReadyOrderPage.java b/wicket-core/src/test/java/org/apache/wicket/ajax/DomReadyOrderPage.java index 4cef699..782cbc5 100644 --- a/wicket-core/src/test/java/org/apache/wicket/ajax/DomReadyOrderPage.java +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/DomReadyOrderPage.java @@ -20,6 +20,7 @@ import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.markup.head.OnDomReadyHeaderItem; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; +import org.apache.wicket.util.lang.Optional; /** * @author jcompagner @@ -60,9 +61,9 @@ public class DomReadyOrderPage extends WebPage } @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { - target.add(this); + target.get().add(this); } } } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_5/AjaxLinkClickTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_5/AjaxLinkClickTest.java b/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_5/AjaxLinkClickTest.java index f8a8e25..b6d6aa5 100644 --- a/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_5/AjaxLinkClickTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_5/AjaxLinkClickTest.java @@ -21,6 +21,7 @@ import org.apache.wicket.WicketTestCase; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.util.lang.Optional; import org.junit.Before; @@ -87,10 +88,10 @@ public class AjaxLinkClickTest extends WicketTestCase private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { linkClicked = true; - ajaxRequestTarget = target; + ajaxRequestTarget = target.getUnsafe(); } }); @@ -115,10 +116,10 @@ public class AjaxLinkClickTest extends WicketTestCase private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { linkClicked = true; - ajaxRequestTarget = target; + ajaxRequestTarget = target.getUnsafe(); } }); http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_6/LinkPage.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_6/LinkPage.java b/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_6/LinkPage.java index 9e6dc21..c129eaa 100644 --- a/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_6/LinkPage.java +++ b/wicket-core/src/test/java/org/apache/wicket/util/tester/apps_6/LinkPage.java @@ -23,6 +23,7 @@ import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.util.lang.Optional; import org.apache.wicket.util.tester.WicketTester; /** @@ -91,7 +92,7 @@ public class LinkPage extends WebPage private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { getRequestCycle().setResponsePage(ResultPage.class); } @@ -102,7 +103,7 @@ public class LinkPage extends WebPage private static final long serialVersionUID = 1L; @Override - public void onClick(AjaxRequestTarget target) + public void onClick(Optional<AjaxRequestTarget> target) { getRequestCycle().setResponsePage(new ResultPage("A special label")); } http://git-wip-us.apache.org/repos/asf/wicket/blob/676bd386/wicket-util/src/main/java/org/apache/wicket/util/lang/Optional.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/lang/Optional.java b/wicket-util/src/main/java/org/apache/wicket/util/lang/Optional.java new file mode 100644 index 0000000..1c33575 --- /dev/null +++ b/wicket-util/src/main/java/org/apache/wicket/util/lang/Optional.java @@ -0,0 +1,256 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.wicket.util.lang; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; + +/** + * Represents an optional value. + * <p> + * This class aids in making handling of possible {@code null} values (whether return values or + * parameter values) explicit and hopefully reduces the number of {@link NullPointerException}s that + * will be thrown. + * </p> + * <p> + * It also makes tracking down NPEs easier by not allowing {@code null} return values to be + * propagated as parameters to further function calls, it does it by making {@link #get()} throw an + * NPE if called on an {@link Optional} constructed with a {@code null} value. + * </p> + * <p> + * By supporting {@link Iterable} this class allows the developer to rewrite code like this: <code> + * if (optional.isSet()) { + * optional.get().foo(); + * optional.get().bar(); + * optional.get().baz(); + * } + * </code> With the following: <code> + * for (Value value:optional) { + * value.foo(); + * value.bar(); + * value.baz(); + * } + * </code> which some developers find easier to read. + * </p> + * + * @author igor + * @param <T> + */ +public final class Optional<T> implements Iterable<T>, Serializable +{ + private static final long serialVersionUID = 1L; + + private static final Optional<Void> NULL = new Optional<Void>(null); + + private static final Iterator<?> EMPTY_ITERATOR = Collections.emptyList().iterator(); + + private final T value; + + private Optional(T value) + { + this.value = value; + } + + /** + * Gets the stored value or throws {@link NullPointerException} if the value is {@code null} + * + * @throws NullPointerException + * if the value is {@code null} + * + * @return stored value + */ + public T get() + { + if (value == null) + { + throw new NullPointerException(); + } + return value; + } + + /** + * Gets the stored value or returns {@code defaultValue} if value is {@code null} + * + * @param defaultValue + * default value + * + * @return stored value or {@code defaultValue} + */ + public T get(T defaultValue) + { + if (value == null) + { + return defaultValue; + } + return value; + } + + /** + * @return {@code true} iff value is not {@code null} + */ + public boolean exists() + { + return value != null; + } + + /** + * @return {code true} if the store value is {@code null} + */ + public boolean isNull() + { + return value == null; + } + + /** + * @return {code true} if the store value is not {@code null} + */ + public boolean isNotNull() + { + return value != null; + } + + /** + * @return {code true} if the store value is not {@code null} + */ + public boolean isSet() + { + return value != null; + } + + /** + * @return {code true} if the store value is {@code null} + */ + public boolean isNotSet() + { + return value == null; + } + + @SuppressWarnings("unchecked") + public Iterator<T> iterator() + { + if (value == null) + { + return (Iterator<T>)EMPTY_ITERATOR; + } + else + { + return Collections.singleton(value).iterator(); + } + } + + /** + * Factory method for creating {@link Optional} values + * + * @param <Z> + * @param value + * @return optional that represents the specified value + */ + @SuppressWarnings("unchecked") + public static <Z> Optional<Z> of(Z value) + { + if (value == null) + { + return (Optional<Z>)NULL; + } + else + { + return new Optional<Z>(value); + } + } + + /** + * Factory method for creating an {@link Optional} value that represents a {@code null} + * + * @param <T> + * @return optional that represents a {@code null} + */ + @SuppressWarnings("unchecked") + public static <Z> Optional<Z> ofNull() + { + return (Optional<Z>)NULL; + } + + /** + * Checks if the stored value is the same as the {@code other} value + * + * @param other + * @return {@code true} iff stored value == other + */ + public boolean isValueSameAs(T other) + { + return value == other; + } + + /** + * A null-safe checks to see if the stored value is equal to the {@code other} value + * + * @param other + * @return {@code true} iff stored value equals other + */ + public boolean isValueEqualTo(T other) + { + return Objects.equal(value, other); + } + + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof Optional)) + { + return false; + } + Optional<?> other = (Optional<?>)obj; + return Objects.equal(value, other.value); + } + + /** + * @return either the String "null" or the String "value: " followed by the contained value's + * toString + */ + @Override + public String toString() + { + return "[Optional value=" + value + "]"; + } + + /** + * Performs an unsafe {@link #get()}, may return {@code null} + * + * @return stored {@code value} including {@code null} + */ + public T getUnsafe() + { + return value; + } +} \ No newline at end of file
