You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by iv...@apache.org on 2010/10/31 00:57:29 UTC
svn commit: r1029219 - in /wicket/trunk/wicket: ./
src/main/java/org/apache/wicket/ src/test/java/org/apache/wicket/behavior/
Author: ivaynberg
Date: Sat Oct 30 22:57:28 2010
New Revision: 1029219
URL: http://svn.apache.org/viewvc?rev=1029219&view=rev
Log:
code cleanup: moved all the behavior collection management junk into a helper object
Added:
wicket/trunk/wicket/Wicket - Tests.launch (with props)
wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java (with props)
wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java (contents, props changed)
- copied, changed from r1028987, wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIndexTest.java
Removed:
wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIndexTest.java
Modified:
wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java
Added: wicket/trunk/wicket/Wicket - Tests.launch
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/Wicket%20-%20Tests.launch?rev=1029219&view=auto
==============================================================================
--- wicket/trunk/wicket/Wicket - Tests.launch (added)
+++ wicket/trunk/wicket/Wicket - Tests.launch Sat Oct 30 22:57:28 2010
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<stringAttribute key="bad_container_name" value="\wicket\regenerate-tests"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/wicket/src/test/java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="2"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=wicket/src\/test\/java"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="wicket"/>
+</launchConfiguration>
Propchange: wicket/trunk/wicket/Wicket - Tests.launch
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java?rev=1029219&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java Sat Oct 30 22:57:28 2010
@@ -0,0 +1,301 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.behavior.IBehavior;
+import org.apache.wicket.behavior.InvalidBehaviorIdException;
+import org.apache.wicket.model.IDetachable;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * Manages behaviors in a {@link Component} instance
+ *
+ * @author igor
+ */
+final class Behaviors implements IDetachable
+{
+ private final Component component;
+
+ public Behaviors(Component component)
+ {
+ this.component = component;
+ }
+
+ public void add(IBehavior... behaviors)
+ {
+ if (behaviors == null)
+ {
+ throw new IllegalArgumentException("Argument may not be null");
+ }
+
+ for (IBehavior behavior : behaviors)
+ {
+ if (behavior == null)
+ {
+ throw new IllegalArgumentException("Argument may not be null");
+ }
+
+ internalAdd(behavior);
+
+ if (!behavior.isTemporary())
+ {
+ component.addStateChange();
+ }
+
+ // Give handler the opportunity to bind this component
+ behavior.bind(component);
+ }
+ }
+
+ private void internalAdd(final IBehavior behavior)
+ {
+ component.data_add(behavior);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <M extends IBehavior> List<M> getBehaviors(Class<M> type)
+ {
+ final int len = component.data_length();
+ final int start = component.data_start();
+ if (len < start)
+ {
+ return Collections.emptyList();
+ }
+
+ List<M> subset = new ArrayList<M>(len);
+ for (int i = component.data_start(); i < len; i++)
+ {
+ Object obj = component.data_get(i);
+ if (obj != null && obj instanceof IBehavior)
+ {
+ if (type == null || type.isAssignableFrom(obj.getClass()))
+ {
+ subset.add((M)obj);
+ }
+ }
+ }
+ return Collections.unmodifiableList(subset);
+ }
+
+
+ public void remove(IBehavior behavior)
+ {
+ if (behavior == null)
+ {
+ throw new IllegalArgumentException("Argument `behavior` cannot be null");
+ }
+
+ if (internalRemove(behavior))
+ {
+ if (!behavior.isTemporary())
+ {
+ component.addStateChange();
+ }
+ behavior.detach(component);
+ }
+ else
+ {
+ throw new IllegalStateException(
+ "Tried to remove a behavior that was not added to the component. Behavior: " +
+ behavior.toString());
+ }
+ }
+
+ /**
+ * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
+ *
+ * Traverses all behaviors and calls detachModel() on them. This is needed to cleanup behavior
+ * after render. This method is necessary for {@link AjaxRequestTarget} to be able to cleanup
+ * component's behaviors after header contribution has been done (which is separated from
+ * component render).
+ */
+ public final void detach()
+ {
+ final int len = component.data_length();
+ for (int i = component.data_start(); i < len; i++)
+ {
+ Object obj = component.data_get(i);
+ if (obj != null && obj instanceof IBehavior)
+ {
+ final IBehavior behavior = (IBehavior)obj;
+
+ behavior.detach(component);
+
+ if (behavior.isTemporary())
+ {
+ internalRemove(behavior);
+ }
+ }
+ }
+ }
+
+ private boolean internalRemove(final IBehavior behavior)
+ {
+ final int len = component.data_length();
+ for (int i = component.data_start(); i < len; i++)
+ {
+ Object o = component.data_get(i);
+ if (o != null && o.equals(behavior))
+ {
+ component.data_remove(i);
+ behavior.unbind(component);
+
+ // remove behavior from behavior-ids
+ ArrayList<IBehavior> ids = getBehaviorsIdList(false);
+ if (ids != null)
+ {
+ int idx = ids.indexOf(behavior);
+ if (idx == ids.size() - 1)
+ {
+ ids.remove(idx);
+ }
+ else if (idx >= 0)
+ {
+ ids.set(idx, null);
+ }
+ ids.trimToSize();
+
+ if (ids.isEmpty())
+ {
+ removeBehaviorsIdList();
+ }
+
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void removeBehaviorsIdList()
+ {
+ for (int i = component.data_start(); i < component.data_length(); i++)
+ {
+ Object obj = component.data_get(i);
+ if (obj != null && obj instanceof BehaviorIdList)
+ {
+ component.data_remove(i);
+ return;
+ }
+ }
+ }
+
+ private BehaviorIdList getBehaviorsIdList(boolean createIfNotFound)
+ {
+ int len = component.data_length();
+ for (int i = component.data_start(); i < len; i++)
+ {
+ Object obj = component.data_get(i);
+ if (obj != null && obj instanceof BehaviorIdList)
+ {
+ return (BehaviorIdList)obj;
+ }
+ }
+ if (createIfNotFound)
+ {
+ BehaviorIdList list = new BehaviorIdList();
+ component.data_add(list);
+ return list;
+ }
+ return null;
+ }
+
+ private static class BehaviorIdList extends ArrayList<IBehavior>
+ {
+ public BehaviorIdList()
+ {
+ super(1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public final int getBehaviorId(IBehavior behavior)
+ {
+ Args.notNull(behavior, "behavior");
+
+ boolean found = false;
+ for (int i = component.data_start(); i < component.data_length(); i++)
+ {
+ if (behavior == component.data_get(i))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ throw new IllegalStateException(
+ "Behavior must be added to component before its id can be generated. Behavior: " +
+ behavior + ", Component: " + this);
+ }
+
+ ArrayList<IBehavior> ids = getBehaviorsIdList(true);
+
+ int id = ids.indexOf(behavior);
+
+ if (id < 0)
+ {
+ // try to find an unused slot
+ for (int i = 0; i < ids.size(); i++)
+ {
+ if (ids.get(i) == null)
+ {
+ ids.set(i, behavior);
+ id = i;
+ break;
+ }
+ }
+ }
+
+ if (id < 0)
+ {
+ // no unused slots, add to the end
+ id = ids.size();
+ ids.add(behavior);
+ ids.trimToSize();
+ }
+
+ return id;
+ }
+
+ public final IBehavior getBehaviorById(int id)
+ {
+ IBehavior behavior = null;
+
+ ArrayList<IBehavior> ids = getBehaviorsIdList(false);
+ if (ids != null)
+ {
+ if (id >= 0 && id < ids.size())
+ {
+ behavior = ids.get(id);
+ }
+ }
+
+ if (behavior != null)
+ {
+ return behavior;
+ }
+ throw new InvalidBehaviorIdException(component, id);
+ }
+
+
+}
Propchange: wicket/trunk/wicket/src/main/java/org/apache/wicket/Behaviors.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java?rev=1029219&r1=1029218&r2=1029219&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/Component.java Sat Oct 30 22:57:28 2010
@@ -18,19 +18,16 @@ package org.apache.wicket;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
-import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.authorization.Action;
import org.apache.wicket.authorization.AuthorizationException;
import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.authorization.UnauthorizedActionException;
import org.apache.wicket.behavior.IBehavior;
-import org.apache.wicket.behavior.InvalidBehaviorIdException;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.event.IEventSink;
@@ -236,12 +233,6 @@ public abstract class Component
private static final long serialVersionUID = 1L;
- /** metadata key that stores stable ids for behavior - ids are used when generating a url, etc */
- private static MetaDataKey<ArrayList<IBehavior>> BEHAVIOR_IDS = new MetaDataKey<ArrayList<IBehavior>>()
- {
- private static final long serialVersionUID = 1L;
- };
-
/**
* Action used with IAuthorizationStrategy to determine whether a component is allowed to be
* enabled.
@@ -491,7 +482,12 @@ public abstract class Component
*/
Object data = null;
- private final int data_length()
+ final int data_start()
+ {
+ return getFlag(FLAG_MODEL_SET) ? 1 : 0;
+ }
+
+ final int data_length()
{
if (data == null)
{
@@ -507,7 +503,7 @@ public abstract class Component
}
}
- private final Object data_get(int index)
+ final Object data_get(int index)
{
if (data == null)
{
@@ -528,7 +524,7 @@ public abstract class Component
}
}
- private final Object data_set(int index, Object object)
+ final Object data_set(int index, Object object)
{
if (index > data_length() - 1)
{
@@ -549,12 +545,12 @@ public abstract class Component
}
}
- private final void data_add(Object object)
+ final void data_add(Object object)
{
data_insert(-1, object);
}
- private final void data_insert(int position, Object object)
+ final void data_insert(int position, Object object)
{
int currentLength = data_length();
if (position == -1)
@@ -603,7 +599,7 @@ public abstract class Component
}
}
- private Object data_remove(int position)
+ Object data_remove(int position)
{
int currentLength = data_length();
@@ -1010,80 +1006,6 @@ public abstract class Component
}
/**
- * Adds a behavior modifier to the component.
- *
- * <p>
- * Note: this method is override to enable users to do things like discussed in <a
- * href="http://www.nabble.com/Why-add%28IBehavior%29-is-final--tf2598263.html#a7248198">this
- * thread</a>.
- * </p>
- *
- * @param behaviors
- * The behavior modifier(s) to be added
- * @return this (to allow method call chaining)
- */
- public Component add(final IBehavior... behaviors)
- {
- if (behaviors == null)
- {
- throw new IllegalArgumentException("Argument may not be null");
- }
-
- for (IBehavior behavior : behaviors)
- {
- if (behavior == null)
- {
- throw new IllegalArgumentException("Argument may not be null");
- }
-
- addBehavior(behavior);
-
- if (!behavior.isTemporary())
- {
- addStateChange();
- }
-
- // Give handler the opportunity to bind this component
- behavior.bind(this);
- }
- return this;
- }
-
- /**
- *
- * @param behavior
- */
- private void addBehavior(final IBehavior behavior)
- {
- data_add(behavior);
- }
-
- private final List<? extends IBehavior> getBehaviorsRawList()
- {
- if (data != null)
- {
- // if the model is set, we must skip it
- final int startIndex = getFlag(FLAG_MODEL_SET) ? 1 : 0;
- int length = data_length();
-
- if (length > startIndex)
- {
- final ArrayList<IBehavior> result = new ArrayList<IBehavior>();
- for (int i = startIndex; i < length; ++i)
- {
- Object o = data_get(i);
- if (o == null || o instanceof IBehavior)
- {
- result.add((IBehavior)o);
- }
- }
- return result;
- }
- }
- return null;
- }
-
- /**
* Called on very component after the page is rendered. It will call onAfterRender for it self
* and its children.
*/
@@ -1300,7 +1222,7 @@ public abstract class Component
detachModels();
// detach any behaviors
- detachBehaviors();
+ new Behaviors(this).detach();
// always detach children because components can be attached
// independently of their parents
@@ -1328,32 +1250,6 @@ public abstract class Component
}
/**
- * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
- *
- * Traverses all behaviors and calls detachModel() on them. This is needed to cleanup behavior
- * after render. This method is necessary for {@link AjaxRequestTarget} to be able to cleanup
- * component's behaviors after header contribution has been done (which is separated from
- * component render).
- */
- public final void detachBehaviors()
- {
- for (IBehavior behavior : getBehaviors())
- {
- // Always detach models, 'accepted' or not. Otherwise, if they
- // are accepted during render, but not here - something can go
- // undetached, and calling isEnabled can also lead to nasty side
- // effects. See for instance Timo's comment on
- // http://issues.apache.org/jira/browse/WICKET-673
- behavior.detach(this);
-
- if (behavior.isTemporary())
- {
- removeBehavior(behavior);
- }
- }
- }
-
- /**
* Detaches all models
*/
public void detachModels()
@@ -1451,17 +1347,6 @@ public abstract class Component
}
/**
- * Gets the currently coupled {@link IBehavior}s as a unmodifiable list. Returns an empty list
- * rather than null if there are no behaviors coupled to this component.
- *
- * @return The currently coupled behaviors as a unmodifiable list
- */
- public final List<? extends IBehavior> getBehaviors()
- {
- return getBehaviors(IBehavior.class);
- }
-
- /**
* @return A path of the form [page-class-name].[page-relative-path]
* @see Component#getPageRelativePath()
*/
@@ -2401,73 +2286,6 @@ public abstract class Component
parent.remove(this);
}
- /**
- * Removes behavior from component
- *
- * @param behavior
- * behavior to remove
- *
- * @return this (to allow method call chaining)
- */
- public Component remove(final IBehavior behavior)
- {
- if (behavior == null)
- {
- throw new IllegalArgumentException("Argument `behavior` cannot be null");
- }
-
- if (removeBehavior(behavior))
- {
- if (!behavior.isTemporary())
- {
- addStateChange();
- }
- }
- else
- {
- throw new IllegalStateException(
- "Tried to remove a behavior that was not added to the component. Behavior: " +
- behavior.toString());
- }
- return this;
- }
-
- /**
- *
- * @param behavior
- * @return true if behavior found and removed
- */
- private boolean removeBehavior(final IBehavior behavior)
- {
- final int len = data_length();
- for (int i = 0; i < len; i++)
- {
- Object o = data_get(i);
- if (o != null && o.equals(behavior))
- {
- data_remove(i);
- behavior.unbind(this);
-
- // remove behavior from behavior-ids metadata
- ArrayList<IBehavior> ids = getMetaData(BEHAVIOR_IDS);
- if (ids != null)
- {
- int idx = ids.indexOf(behavior);
- if (idx == ids.size() - 1)
- {
- ids.remove(idx);
- }
- else if (idx >= 0)
- {
- ids.set(idx, null);
- }
- ids.trimToSize();
- return true;
- }
- }
- }
- return false;
- }
/**
* Render the Component.
@@ -3687,28 +3505,7 @@ public abstract class Component
@SuppressWarnings("unchecked")
public <M extends IBehavior> List<M> getBehaviors(Class<M> type)
{
- List<? extends IBehavior> behaviors = getBehaviorsRawList();
- if (behaviors == null)
- {
- return Collections.emptyList();
- }
-
- List<M> subset = new ArrayList<M>(behaviors.size()); // avoid growing
- for (IBehavior behavior : behaviors)
- {
- if (behavior != null)
- {
- if (type == null)
- {
- subset.add((M)behavior);
- }
- else if (type.isAssignableFrom(behavior.getClass()))
- {
- subset.add(type.cast(behavior));
- }
- }
- }
- return Collections.unmodifiableList(subset);
+ return new Behaviors(this).getBehaviors(type);
}
/**
@@ -4493,69 +4290,60 @@ public abstract class Component
new ComponentEventSender(this).send(sink, type, payload);
}
+ /**
+ * Removes behavior from component
+ *
+ * @param behavior
+ * behavior to remove
+ *
+ * @return this (to allow method call chaining)
+ */
+ public Component remove(final IBehavior behavior)
+ {
+ new Behaviors(this).remove(behavior);
+ return this;
+ }
+
/** {@inheritDoc} */
public final IBehavior getBehaviorById(int id)
{
- IBehavior behavior = null;
-
- ArrayList<IBehavior> ids = getMetaData(BEHAVIOR_IDS);
- if (ids != null)
- {
- if (id >= 0 && id < ids.size())
- {
- behavior = ids.get(id);
- }
- }
-
- if (behavior != null)
- {
- return behavior;
- }
- throw new InvalidBehaviorIdException(this, id);
+ return new Behaviors(this).getBehaviorById(id);
}
/** {@inheritDoc} */
public final int getBehaviorId(IBehavior behavior)
{
- if (!getBehaviorsRawList().contains(behavior))
- {
- throw new IllegalStateException(
- "Behavior must be added to component before its id can be generated. Behavior: " +
- behavior + ", Component: " + this);
- }
-
- ArrayList<IBehavior> ids = getMetaData(BEHAVIOR_IDS);
-
- if (ids == null)
- {
- ids = new ArrayList<IBehavior>(1);
- setMetaData(BEHAVIOR_IDS, ids);
- }
-
- int id = ids.indexOf(behavior);
-
- if (id < 0)
- {
- // try to find an unused slot
- for (int i = 0; i < ids.size(); i++)
- {
- if (ids.get(i) == null)
- {
- ids.set(i, behavior);
- id = i;
- }
- }
- }
+ return new Behaviors(this).getBehaviorId(behavior);
+ }
- if (id < 0)
- {
- // no unused slots, add to the end
- id = ids.size();
- ids.add(behavior);
- ids.trimToSize();
- }
+ /**
+ * Adds a behavior modifier to the component.
+ *
+ * <p>
+ * Note: this method is override to enable users to do things like discussed in <a
+ * href="http://www.nabble.com/Why-add%28IBehavior%29-is-final--tf2598263.html#a7248198">this
+ * thread</a>.
+ * </p>
+ *
+ * @param behaviors
+ * The behavior modifier(s) to be added
+ * @return this (to allow method call chaining)
+ */
+ public Component add(final IBehavior... behaviors)
+ {
+ new Behaviors(this).add(behaviors);
+ return this;
+ }
- return id;
+ /**
+ * Gets the currently coupled {@link IBehavior}s as a unmodifiable list. Returns an empty list
+ * rather than null if there are no behaviors coupled to this component.
+ *
+ * @return The currently coupled behaviors as a unmodifiable list
+ */
+ public final List<? extends IBehavior> getBehaviors()
+ {
+ return getBehaviors(IBehavior.class);
}
}
Copied: wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java (from r1028987, wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIndexTest.java)
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java?p2=wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java&p1=wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIndexTest.java&r1=1028987&r2=1029219&rev=1029219&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIndexTest.java (original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java Sat Oct 30 22:57:28 2010
@@ -29,7 +29,7 @@ import org.apache.wicket.util.resource.I
import org.apache.wicket.util.resource.StringResourceStream;
/** IBehavior array management tests */
-public class ImmutableBehaviorIndexTest extends WicketTestCase
+public class ImmutableBehaviorIdsTest extends WicketTestCase
{
/** Tests simple behavior */
public void testSimple()
@@ -59,6 +59,7 @@ public class ImmutableBehaviorIndexTest
tester.startPage(page);
String output = tester.getLastResponseAsString();
+ System.out.println(output);
assertTrue(output.contains("class=\"border\""));
assertTrue(output.contains("autocomplete=\"off\""));
assertTrue(output.contains("class2=\"border\""));
Propchange: wicket/trunk/wicket/src/test/java/org/apache/wicket/behavior/ImmutableBehaviorIdsTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain