Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
XWiki
xwiki-commons
Commits
758f198f
Commit
758f198f
authored
Oct 28, 2012
by
Andreas Jonsson
Browse files
XCOMMONS-278: Changed to builder pattern for declaring a property in the execution context.
parent
17d5092d
Changes
6
Hide whitespace changes
Inline
Side-by-side
xwiki-commons-core/xwiki-commons-context/src/main/java/org/xwiki/context/ExecutionContext.java
View file @
758f198f
...
...
@@ -25,6 +25,8 @@
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.xwiki.context.internal.ExecutionContextProperty
;
/**
* Contains all state data related to the current user action. Note that the execution context is independent of the
* environment and all environment-dependent data are stored in the Container component instead.
...
...
@@ -58,6 +60,16 @@ public Object getProperty(String key)
return
property
.
getValue
();
}
/**
* @param key the key of the property.
* @return a builder object for performing the declaration. The property will not be declared until the declare
* method is called on the builder object.
*/
public
DeclarationBuilder
newProperty
(
String
key
)
{
return
new
DeclarationBuilder
(
key
);
}
/**
* @param key the key under which is stored the property to retrieve
* @return {@code true} if there is a property declared for the given key.
...
...
@@ -110,8 +122,8 @@ public void setProperty(String key, Object value)
if
(
property
==
null
)
{
LOGGER
.
debug
(
"Implicit declaration of property {}."
,
key
);
property
=
new
ExecutionContext
Property
(
key
);
propert
ies
.
put
(
key
,
propert
y
);
new
Property
(
key
)
.
declare
()
;
propert
y
=
properties
.
get
(
ke
y
);
}
else
if
(
property
.
isFinal
())
{
throw
new
PropertyIsFinalException
(
key
);
}
...
...
@@ -138,7 +150,7 @@ public void setProperties(Map<String, Object> properties)
*
* @since 4.3M1
*/
p
ublic
void
declareProperty
(
ExecutionContextProperty
property
)
p
rivate
void
declareProperty
(
ExecutionContextProperty
property
)
{
if
(
properties
.
containsKey
(
property
.
getKey
()))
{
throw
new
PropertyAlreadyExistsException
(
property
.
getKey
());
...
...
@@ -176,6 +188,18 @@ public void inheritFrom(ExecutionContext executionContext)
}
}
/**
* This method is intentionally non-public and should only be used for unit testing the property objects.
*
* @param key The property key.
* @return The declared property.
* @since 4.3M2
*/
ExecutionContextProperty
fetchProperty
(
String
key
)
{
return
properties
.
get
(
key
);
}
/**
* @param property Property to check.
* @throws IllegalStateException if the property may not be ignored.
...
...
@@ -192,4 +216,121 @@ private void checkIfInheritedPropertyMayBeIgnored(ExecutionContextProperty prope
}
}
}
/**
* Builder class for declaring a new proprety.
*
* @since 4.3M2
*/
public
final
class
DeclarationBuilder
{
/** @see ExecutionContextProperty#key */
private
final
String
key
;
/** @see ExecutionContextProperty#value */
private
Object
value
;
/** @see ExecutionContextProperty#cloneValue */
private
boolean
cloneValue
;
/** @see ExecutionContextProperty#isFinal */
private
boolean
isFinal
;
/** @see ExecutionContextProperty#inherited */
private
boolean
inherited
;
/** @see ExecutionContextProperty#nonNull */
private
boolean
nonNull
;
/** @see ExecutionContextProperty#type */
private
Class
<?>
type
;
/**
* Start building a property for the given key.
*
* @param key The property key.
*/
private
DeclarationBuilder
(
String
key
)
{
this
.
key
=
key
;
}
/**
* Finish the building by declaring the property in this execution context.
*/
public
void
declare
()
{
ExecutionContext
.
this
.
declareProperty
(
new
ExecutionContextProperty
(
key
,
value
,
cloneValue
,
isFinal
,
inherited
,
nonNull
,
type
));
}
/**
* @param value The initial value.
* @return this declaration builder.
*/
public
DeclarationBuilder
initial
(
Object
value
)
{
this
.
value
=
value
;
return
this
;
}
/**
* Make the initial value the final value.
*
* @return this declaration builder.
*/
public
DeclarationBuilder
makeFinal
()
{
isFinal
=
true
;
return
this
;
}
/**
* Indicate that the value should be cloned when the property is cloned.
*
* @return this declaration builder.
*/
public
DeclarationBuilder
cloneValue
()
{
cloneValue
=
true
;
return
this
;
}
/**
* Set the type of the value.
*
* @param type The type to declare for the property.
*
* @return this declaration builder.
*/
public
DeclarationBuilder
type
(
Class
<?>
type
)
{
this
.
type
=
type
;
return
this
;
}
/**
* Indicate that the property should be inherited.
*
* @return this declaration builder.
*/
public
DeclarationBuilder
inherited
()
{
inherited
=
true
;
return
this
;
}
/**
* Indicate that the property value may not be {@literal null}.
*
* @return this declaration builder.
*/
public
DeclarationBuilder
nonNull
()
{
nonNull
=
true
;
return
this
;
}
}
}
xwiki-commons-core/xwiki-commons-context/src/main/java/org/xwiki/context/ExecutionContextProperty.java
→
xwiki-commons-core/xwiki-commons-context/src/main/java/org/xwiki/context/
internal/
ExecutionContextProperty.java
View file @
758f198f
...
...
@@ -17,7 +17,7 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package
org.xwiki.context
;
package
org.xwiki.context
.internal
;
import
java.lang.ref.WeakReference
;
import
java.lang.reflect.InvocationTargetException
;
...
...
@@ -33,60 +33,84 @@
public
class
ExecutionContextProperty
implements
Cloneable
{
/** The key that is the name of this property in the execution context. */
private
String
key
;
private
final
String
key
;
/** This is the actual property value. */
private
Object
value
;
/**
* Clone the value when this property is cloned.
*
* ReflectPermission suppressAccessChecks may be required.
*/
private
boolean
cloneValue
;
private
final
boolean
cloneValue
;
/** Controls whether the value of this property is final. */
private
boolean
isFinal
;
private
final
boolean
isFinal
;
/**
* Controls whether this property should be inherited across execution contexts. It will be inherited as long as
* an execution context containing the property is the current execution context. It will not be propagated to
* parent execution contexts. Hence, it may be removed by a call to popExecutionContext.
*/
private
boolean
inherited
;
private
final
boolean
inherited
;
/** Indicate that the value may not be {@code null}. */
private
boolean
nonNull
;
private
final
boolean
nonNull
;
/** The type of the value. */
private
Class
<?>
type
;
private
final
Class
<?>
type
;
/** @see isClonedFrom(ExecutionContextProperty property). */
private
WeakReference
<
ExecutionContextProperty
>
clonedFrom
;
/**
* @param key The execution context key.
* @param initialValue The initial value.
* @param cloneValue Indicate that the value should be cloned when the property is cloned.
* @param isFinal Indicate that the value may not be updated from the initial value.
* @param inherited Indicate that the property should be inherited when activating a new execution context.
* @param nonNull Indicate that the property value may not be {@literal null}.
* @param type Set a class which the value must be assignable to.
*/
public
ExecutionContextProperty
(
String
key
)
public
ExecutionContextProperty
(
String
key
,
Object
initialValue
,
boolean
cloneValue
,
boolean
isFinal
,
boolean
inherited
,
boolean
nonNull
,
Class
<?>
type
)
{
this
.
key
=
key
;
this
.
value
=
initialValue
;
this
.
cloneValue
=
cloneValue
;
this
.
isFinal
=
isFinal
;
this
.
inherited
=
inherited
;
this
.
nonNull
=
nonNull
;
this
.
type
=
type
;
checkValue
(
initialValue
);
}
/**
* @param value The object value.
* Check that the value is compatible with the configure constraints.
*
* @param value The value.
* @throws IllegalArgumentException if the value is null and this property has the nonNull attribute set, or if the
* type is set for this value, but the value is not assignable to the set type.
*/
p
ublic
void
set
Value
(
Object
value
)
p
rivate
void
check
Value
(
Object
value
)
{
if
(
nonNull
&&
value
==
null
)
{
throw
new
IllegalArgumentException
(
String
.
format
(
"The property [%s] may not be null!"
,
key
));
}
if
(
type
!=
null
&&
!
type
.
isAssignableFrom
(
value
.
getClass
()))
{
if
(
type
!=
null
&&
value
!=
null
&&
!
type
.
isAssignableFrom
(
value
.
getClass
()))
{
throw
new
IllegalArgumentException
(
String
.
format
(
"The value of property [%s] must be of type [%s], but was [%s]"
,
key
,
type
,
value
.
getClass
()));
}
}
/**
* @param value The object value.
* @throws IllegalArgumentException if the value is null and this property has the nonNull attribute set, or if the
* type is set for this value, but the value is not assignable to the set type.
*/
public
void
setValue
(
Object
value
)
{
checkValue
(
value
);
this
.
value
=
value
;
}
...
...
@@ -102,61 +126,28 @@ public String getKey()
return
this
.
key
;
}
/** @param cloneValue Set wether the value should be cloned when this property is cloned. */
public
void
setCloneValue
(
boolean
cloneValue
)
{
this
.
cloneValue
=
cloneValue
;
}
/** @param isFinal Set wether the property is final or not. */
public
void
setFinal
(
boolean
isFinal
)
{
this
.
isFinal
=
isFinal
;
}
/** @return wether this property is final or not. */
public
boolean
isFinal
()
{
return
this
.
isFinal
;
}
/** @param inherited Set wether this property should be inherited across execution contexts or not. */
public
void
setInherited
(
boolean
inherited
)
{
this
.
inherited
=
inherited
;
}
/** @return wether this property should be inherited across execution contexts or not. */
public
boolean
isInherited
()
{
return
this
.
inherited
;
}
/** @param nonNull Indicate wether it is an error if th value of this property is set to {@code null} or not. */
public
void
setNonNull
(
boolean
nonNull
)
{
this
.
nonNull
=
nonNull
;
}
/** @param type The type of this property's value. If set, the value will be typechecked when set. */
public
void
setType
(
Class
<?>
type
)
{
this
.
type
=
type
;
}
@Override
public
ExecutionContextProperty
clone
()
{
ExecutionContextProperty
clone
;
try
{
clone
=
(
ExecutionContextProperty
)
super
.
clone
();
}
catch
(
CloneNotSupportedException
e
)
{
throw
new
RuntimeException
(
e
);
}
Object
clonedValue
;
if
(
cloneValue
)
{
try
{
clone
.
v
alue
=
value
.
getClass
().
getMethod
(
"clone"
).
invoke
(
value
);
clone
dV
alue
=
value
.
getClass
().
getMethod
(
"clone"
).
invoke
(
value
);
}
catch
(
NoSuchMethodException
e
)
{
throw
new
IllegalStateException
(
String
.
format
(
"cloneValue attribute was set on property [%s], "
...
...
@@ -168,14 +159,11 @@ public ExecutionContextProperty clone()
throw
new
RuntimeException
(
e
);
}
}
else
{
clone
.
v
alue
=
value
;
clone
dV
alue
=
value
;
}
clone
.
key
=
key
;
clone
.
cloneValue
=
cloneValue
;
clone
.
isFinal
=
isFinal
;
clone
.
inherited
=
inherited
;
clone
.
nonNull
=
nonNull
;
clone
.
type
=
type
;
clone
=
new
ExecutionContextProperty
(
key
,
clonedValue
,
cloneValue
,
isFinal
,
inherited
,
nonNull
,
type
);
if
(
isFinal
&&
inherited
)
{
// We make this a weak reference, because we are only interested in it as long
// as it is references by the current execution co
...
...
@@ -195,7 +183,7 @@ public ExecutionContextProperty clone()
* @param property The original property.
* @return If the return value is {@code true}, then this property was cloned from the given property.
*/
boolean
isClonedFrom
(
ExecutionContextProperty
property
)
public
boolean
isClonedFrom
(
ExecutionContextProperty
property
)
{
return
clonedFrom
!=
null
&&
clonedFrom
.
get
()
==
property
;
}
...
...
xwiki-commons-core/xwiki-commons-context/src/test/java/org/xwiki/context/ExecutionContextPropertyTest.java
View file @
758f198f
...
...
@@ -32,105 +32,99 @@
@Test
public
void
defaultValues
()
throws
Exception
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
)
;
final
String
key
=
"test"
;
Assert
.
assertFalse
(
property
.
isFinal
());
Assert
.
assertTrue
(
"test"
.
equals
(
property
.
getKey
()));
Assert
.
assertTrue
(
null
==
property
.
getValue
());
Assert
.
assertFalse
(
property
.
isInherited
());
ExecutionContext
context
=
new
ExecutionContext
();
context
.
newProperty
(
key
).
declare
();
Assert
.
assertFalse
(
context
.
fetchProperty
(
key
).
isFinal
());
Assert
.
assertTrue
(
key
.
equals
(
context
.
fetchProperty
(
key
).
getKey
()));
Assert
.
assertTrue
(
null
==
context
.
fetchProperty
(
key
).
getValue
());
Assert
.
assertFalse
(
context
.
fetchProperty
(
key
).
isInherited
());
Object
o
=
new
Object
();
property
.
setValue
(
o
);
Assert
.
assertTrue
(
p
roperty
.
getValue
()
==
o
);
property
.
setValue
(
null
);
Assert
.
assertTrue
(
p
roperty
.
getValue
()
==
null
);
context
.
setProperty
(
key
,
o
);
Assert
.
assertTrue
(
context
.
fetchP
roperty
(
key
)
.
getValue
()
==
o
);
context
.
setProperty
(
key
,
null
);
Assert
.
assertTrue
(
context
.
fetchP
roperty
(
key
)
.
getValue
()
==
null
);
}
@Test
public
void
cloning
()
throws
Exception
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
)
;
TestCloneable
value
=
new
TestCloneable
()
;
final
String
k1
=
"test
1
"
;
final
String
k2
=
"test2"
;
final
String
k3
=
"test3"
;
property
.
setValue
(
value
);
ExecutionContext
context
=
new
ExecutionContext
(
);
ExecutionContextProperty
clone
=
property
.
clon
e
();
TestCloneable
value
=
new
TestCloneabl
e
();
Assert
.
assertTrue
(
value
==
clone
.
getValu
e
()
)
;
context
.
newProperty
(
k1
).
initial
(
value
).
declar
e
();
property
.
setClone
Value
(
true
);
Assert
.
assertTrue
(
value
==
context
.
fetchProperty
(
k1
).
clone
().
get
Value
(
)
);
c
l
on
e
=
property
.
clon
e
();
con
text
.
newProperty
(
k2
).
initial
(
value
).
cloneValue
().
declar
e
();
Assert
.
assertTrue
(
value
!=
clone
.
get
Value
()
&&
(
(
TestCloneable
)
c
l
on
e
.
getValue
()).
value
.
equals
(
"clone"
)
);
TestCloneable
clone
d
Value
=
(
TestCloneable
)
con
text
.
fetchProperty
(
k2
).
clone
().
getValue
(
);
property
.
setFinal
(
true
);
property
.
setInherited
(
true
);
Assert
.
assertTrue
(
value
!=
clonedValue
&&
clonedValue
.
value
.
equals
(
"clone"
));
c
l
on
e
=
property
.
clon
e
();
con
text
.
newProperty
(
k3
).
initial
(
value
).
cloneValue
().
makeFinal
().
inherited
().
declar
e
();
Assert
.
assertTrue
(
clone
.
isClonedFrom
(
p
roperty
));
Assert
.
assertTrue
(
context
.
fetchProperty
(
k3
).
clone
()
.
isClonedFrom
(
context
.
fetchP
roperty
(
k3
)
));
}
@Test
(
expected
=
IllegalStateException
.
class
)
public
void
cloningNonPublicCloneMethod
()
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
);
TestNonpublicClone
value
=
new
TestNonpublicClone
();
property
.
setCloneValue
(
true
);
property
.
setValue
(
value
);
ExecutionContext
context
=
new
ExecutionContext
();
property
.
clone
();
}
@Test
public
void
assertClonedFrom
()
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
);
final
String
key
=
"test"
;
property
.
setFinal
(
true
);
property
.
setInherited
(
true
);
TestNonpublicClone
value
=
new
TestNonpublicClone
();
ExecutionC
ontextProperty
clone
=
property
.
clon
e
();
c
ontext
.
new
Property
(
key
).
cloneValue
().
initial
(
value
).
declar
e
();
Assert
.
assertFalse
(
clone
.
isClonedFrom
(
clone
)
);
context
.
fetchProperty
(
key
).
clone
(
);
}
@Test
(
expected
=
IllegalArgumentException
.
class
)
public
void
nonNullCheck
()
{
ExecutionContext
Property
property
=
new
ExecutionContext
Property
(
"test"
);
ExecutionContext
context
=
new
ExecutionContext
(
);
property
.
setNonNull
(
true
)
;
final
String
key
=
"test"
;
property
.
setValue
(
null
);
context
.
newProperty
(
key
).
nonNull
().
initial
(
null
).
declare
(
);
}
@Test
public
void
typeChecking
()
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
);
ExecutionContext
context
=
new
ExecutionContext
();
final
String
key
=
"test"
;
property
.
setT
ype
(
SomeClass
.
class
);
context
.
newProperty
(
key
).
t
ype
(
SomeClass
.
class
)
.
declare
()
;
property
.
setValue
(
new
SomeClass
());
property
.
setValue
(
new
SomeSubClass
());
context
.
setProperty
(
key
,
new
SomeClass
());
context
.
setProperty
(
key
,
new
SomeSubClass
());
}
@Test
(
expected
=
IllegalArgumentException
.
class
)
public
void
typeCheckingMismatch
()
{
ExecutionContextProperty
property
=
new
ExecutionContextProperty
(
"test"
);
ExecutionContext
context
=
new
ExecutionContext
();
final
String
key
=
"test"
;
property
.
setT
ype
(
SomeSubClass
.
class
);
context
.
newProperty
(
key
).
t
ype
(
SomeSubClass
.
class
)
.
declare
()
;
property
.
setValue
(
new
SomeClass
());
context
.
setProperty
(
key
,
new
SomeClass
());
}
p
rivate
static
class
TestCloneable
implements
Cloneable
p
ublic
static
class
TestCloneable
implements
Cloneable
{
public
String
value
=
"original"
;
...
...
@@ -144,7 +138,7 @@ public Object clone() throws CloneNotSupportedException
}
}
p
rivate
static
class
TestNonpublicClone
implements
Cloneable
p
ublic
static
class
TestNonpublicClone
implements
Cloneable
{
@Override
protected
Object
clone
()
throws
CloneNotSupportedException
...
...
@@ -153,11 +147,11 @@ protected Object clone() throws CloneNotSupportedException
}
}
p
rivate
static
class
SomeClass
p
ublic
static
class
SomeClass
{
}
p
rivate
static
class
SomeSubClass
extends
SomeClass
p
ublic
static
class
SomeSubClass
extends
SomeClass
{
}
...
...
xwiki-commons-core/xwiki-commons-context/src/test/java/org/xwiki/context/ExecutionContextTest.java
View file @
758f198f
...
...
@@ -36,37 +36,16 @@ public void inheritance()
ExecutionContext
context
=
new
ExecutionContext
();
ExecutionContext
parent
=
new
ExecutionContext
();
ExecutionContextProperty
inherited
=
new
ExecutionContextProperty
(
"inherited"
);
inherited
.
setInherited
(
true
);
inherited
.
setValue
(
"test"
);