Map
and List
.
Note, the default implementation does not have the full support of several methods:
public class MyService { private static final WeakKeyFactory f = new WeakKeyFactory(MyService.class); public static final WeakKey<String> NAME = f.newKey(); public static final WeakKey<Date> BORN = f.newKey(); public static final WeakKey<Boolean> WIFE = f.newKeyDefault(Boolean.TRUE); public static final WeakKey<BigDecimal> CASH = f.newKeyDefault(BigDecimal.ZERO); static { f.lock(); // Initialize all keys and lock them } /** Sample how to use weak keys with a Map. */ public void testWeakKeys2Map() { Map<String,Object> map = new HashMap<String, Object>(); assert NAME.of(map) == null; assert BORN.of(map) == null; assert WIFE.of(map) == Boolean.TRUE; assert CASH.of(map) == BigDecimal.ZERO; final String name = "Lucy"; final Boolean wife = true; final Date today = new Date(); final BigDecimal cash = BigDecimal.TEN; NAME.setValue(map, name); BORN.setValue(map, today); WIFE.setValue(map, wife); CASH.setValue(map, cash); assert NAME.of(map).equals(name); assert BORN.of(map).equals(today); assert WIFE.of(map).equals(wife); assert CASH.of(map).equals(cash); } /** Similar sample how to use weak keys with a List. */ public void testWeakKeys2List() { List<Object> list = new ArrayList<Object>(); // and the same code works for the List instance: assert NAME.of(list) == null; assert BORN.of(list) == null; assert WIFE.of(list) == Boolean.TRUE; assert CASH.of(list) == BigDecimal.ZERO; final String name = "Lucy"; final Boolean wife = true; final Date today = new Date(); final BigDecimal cash = BigDecimal.TEN; NAME.setValue(list, name); BORN.setValue(list, today); WIFE.setValue(list, wife); CASH.setValue(list, cash); assert NAME.of(list).equals(name); assert BORN.of(list).equals(today); assert WIFE.of(list).equals(wife); assert CASH.of(list).equals(cash); } }The next example shows some interesting features of the Key object.
/** Test key attributes */ public void testWeakKeyAttributes() { assert NAME.getIndex()==0; assert BORN.getIndex()==1; assert WIFE.getIndex()==2; assert CASH.getIndex()==3; assert NAME.getName().equals("name"); assert BORN.getName().equals("born"); assert WIFE.getName().equals("wife"); assert CASH.getName().equals("cash"); assert NAME.isTypeOf(CharSequence.class); assert BORN.isTypeOf(Date.class); assert WIFE.isTypeOf(Boolean.class); assert CASH.isTypeOf(BigDecimal.class); }
AbstractAplicationContextAdapter
to get Spring services by the WeakKey, see the examplepublic class Person extends AbstractUjo { private static final KeyFactory<Person> f = newFactory(Person.class); public static final Key<Person,String > NAME = f.newKey(); public static final Key<Person,Boolean> MALE = f.newKey(); public static final Key<Person,Double > CASH = f.newKey(); @Override public KeyList<?> readKeys() { return f.getKeys(); } }or more easy implementation without the class KeyFactory for a child of the class QuickUjo:
public class Person extends QuickUjo {Now, when the Person class is done, we can make its instance and enter or fetch some data. It´s possible to use one of two API methods for writing the object attributes:
public static final Key<Person,String > NAME = newKey(); public static final Key<Person,Boolean> MALE = newKey(); public static final Key<Person,Double > CASH = newKey();
}
import static org.Person.*; Person person = new Person();There is possible to use an extended API with a more obvious source code to the property access since the UJO release 0.80. The new solution allows to you a chaining more keys according to the model of a language Groovy. The new API extends the old one so that you can use an combination of both types.
// Writing: NAME.setValue(person, "Pavel Ponec"); MALE.setValue(person, true); CASH.setValue(person, 34.5);
// Reading: String name = NAME.of(person); // method of() is an alias for Key.getValue() boolean male = MALE.of(person); double cash = CASH.of(person);
import static org.Person.*; Person person = new Person(); // An extension of MapUjoExtA note:
// Writing: person.set(NAME, "Pavel Ponec"); person.set(MALE, true); person.set(CASH, 34.5);
// Reading: String name = person.get(NAME); boolean male = person.get(MALE); double cash = person.get(CASH);
public boolean equals(Object obj) { return UjoManager.getInstanceManager().equals(this, (Ujo) obj ); }
public Object clone() { return UjoManager.getInstanceManager().clone(this, 1, null); }
public int hash() { return UjoManager.getInstanceManager().getHash(this); }All of these implementations are generally applicable to all the Ujo objects posterity.
public interface MyUjoInterface extends Ujo { /** The factory will be an immutable object after locking */ public static final KeyFactory<MyUjoInterface> $factory = KeyFactory.CamelBuilder.get(MyUjoInterface.class); public static final Key<MyUjoInterface,Long> PRO_P0 = $factory.newKey(); public static final Key<MyUjoInterface,Integer> PRO_P1 = $factory.newKey(); public static final Key<MyUjoInterface,String> PRO_P2 = $factory.newKey(); public static final Key<MyUjoInterface,Date> PRO_P3 = $factory.newKey(); public static final ListKey<MyUjoInterface,Float> PRO_P4 = $factory.newListKey(); /** Size of the all keys and lock internal factory. */ public static final int KEY_SIZE = $factory.lockAndSize(); }The interface
MyUjoInterface
can be a child of another Ujo interface optionally,
however an implementation of multiple interfaces is not recommended due to the risk of duplicate Key indexes.
The next example shows a sample of a real implementation:
public class MyUjoImpl implements MyUjoInterface { private Object[] data = new Object[KEY_SIZE]; @Override public KeyList<?> readKeys() { return $factory.getKeys(); } @Override public Object readValue(Key property) { return data[property.getIndex()]; } @Override public void writeValue(Key property, Object value) { data[property.getIndex()] = value; } @Override public boolean readAuthorization(UjoAction action, Key property, Object value) { return true; } /** The deprecated method will be removed in the next Ujo release */ public UjoPropertyList readProperties() { return new UjoPropertyListImpl(readKeys()); } }Note, that a method
readProperties()
can be removed in a next Ujorm release.
{ private Double cash = 10.0; }Also the UJO property can have got a default value. All the implementations of method Ujo.readValue(...) from this frameworks replaces an undefined value (NULL) by the default value from the Key.
{ public static final Key<Person, Double> CASH = newKey("Cash", 10.0); }Note, you can change a default key name also using the first text parameter. The default property name have got
Ujo Validation | Bean Validation (JSR 303) |
---|---|
|
|
Integer maxValidValue = 10; Integer wrongValue = 120; Validator<Integer> maxValidator = Validator.Build.max(maxValidValue); ValidationError error = maxValidator.validate(wrongValue, null, null); // Get the localized message using a template:: String myTemplate = "My input must not be great then ${LIMIT}."; String message = error.getMessage(myTemplate); assertEquals("My input must not be great then 10.", message); // Or get the default message: String defaultMessage = error.getDefaultMessage();If the validation check is correct, then the method Validator.validate(..) returns the NULL value, else the method returns an instance of the ValidationError class. Each the instance of the class ValidationError provides the default validation message and offers tools to create a localized message using a user template with named parameters with the format: ${argument,format}, where the optional format phrase have got the same syntax as the tool {@link java.util.Formatter}. Each Ujorm validator contains at least three argument values:
import static org.ujorm.validator.impl.BetweenValidator.*; ... Integer minValidValue = 1; Integer maxValidValue = 9; Integer wrongValue = 130; Validator<Integer> rangeValidator = Validator.Build.range(minValidValue, maxValidValue); ValidationError error = rangeValidator.validate(wrongValue, ValidBo.CODE, null); // String expectedDefaultMesage = "An attribute ValidBo.code must be between 1 and 9 (including)" + ", but the input is: 130"; assertEquals(expectedDefaultMesage, error.getDefaultMessage()); // String expectedMyMesage = "My input is not between 1 and 9"; String myTemplate = "My input is not between ${MIN} and ${MAX}"; String message = error.getMessage(myTemplate, Locale.ENGLISH); assertEquals(expectedMyMesage, message); // Object[] arrayTemplate = {"My input is not between ", MIN, " and ", MAX}; assertEquals(expectedMyMesage, error.getMessage(arrayTemplate, Locale.ENGLISH)); assertEquals(myTemplate, new MessageService().template(myTemplate));
public class ValidBo extends AbstractUjo { /** Factory */ private static final KeyFactory<ValidBo> f = newFactory(ValidBo.class); /** Documentation: */ public static final Key<ValidBo, Long> PID = f.newKey(notNull()); public static final Key<ValidBo, Integer> CODE = f.newKey(between(0, 10)); public static final Key<ValidBo, String> NAME = f.newKey(regexp("T.*T")); public static final Key<ValidBo, Double> CASH = f.newKey(min(0.0).and(notNull())); static { f.lock(); } }The most of Ujorm Validators allows the null value, however there is possible to join one validator with another one, see the key name "CASH" for example how to do it. There are four methods to create a non-null validator: Build.notNull(), Bulid.notEmpty(), Build.notBlank() and Build.readOnly().
public class BetweenValidator<VALUE extends Comparable> extends AbstractValidator<VALUE> { /** Serializable minimum (inclusive) */ public static final MessageArg MIN = new MessageArg("MIN"); /** Serializable maximum (exclusive) */ public static final MessageArg MAX = new MessageArg("MAX"); /** Serializable minimum (inclusive) */ protected final Comparable min; /** Serializable maximum (exclusive) */ protected final Comparable max; /** * Between validator * @param min Serializable minimum (inclusive) * @param max Serializable maximum (exclusive) */ public BetweenValidator(VALUE min, VALUE max) { this.min = min; this.max = max; } /** {@Inherited} */ public <UJO extends Ujo> ValidationError validate(VALUE input , Key<UJO, VALUE> key , UJO bo ) { final boolean ok = input==null || input.compareTo(min) >= 0 && input.compareTo(max) < 0; return !ok ? new ValidationError ( input , key , bo , getClass() , getLocalizationKey() , getDefaultTemplate() , service.map ( MIN, min , MAX, max )) : null; } /** Returns a default message */ @Override protected String getDefaultTemplate() { return service.template("An attribute ", KEY, " must be between " , MIN, " and ", MAX, " (excluding), but the input is: ", INPUT); } /** Default value is: "org.ujorm.between" */ public String getLocalizationKey() { return KEY_PREFIX + "between"; } }See the JavaDoc for some more validator implementations.
public class Person extends QuickUjo {There is possible to use an special implementation BeanUjo to an implementation UJO features to your complete JavaBeans. This BeanUjo implementation calls JavaBean setters and getters by a Java reflection for writing and reading values (fields). A name of Ujo property must accord with a related name of the JavaBean attribute (methods) in the implementation. There is allowed to use a primitive types of the object fields if you can (e.g. Double -> double).
public static final Key<Person, String> NAME = newKey(); public static final Key<Person, Double> CASH = newKey();
public void setName(String name) { NAME.setValue(this, name); } public String getName() { return NAME.getValue(this); } public void setCash(Double cash) { CASH.setValue(this, cash); } public Double getCash() { return CASH.getValue(this); } }
import org.ujorm.implementation.bean.*; public class Person extends BeanUjo {Keep the mind that getters of implementation BeanUjo don't call a method Ujo.readProperty(..) similar like setters don't call Ujo.writeProperty(). For example this is the reason why method getCash() can't return a default value from Key for example. The way NAME.getValue(...) works fine.
public static final BeanProperty<Person, String> NAME = newKey(); public static final BeanProperty<Person, Double> CASH = newKey(); private String myname; private Double mycash; public void setName(String name) { this.myname = name; } public String getName() { return myname; } public void setCash(Double cash) { this.mycash = cash; } public Double getCash() { return mycash; } }
Person person = somePersonProvider().getPerson();A content of created XML is now:
// Make Serialization: UjoManagerXML.getInstance().saveXML(writer, person, null, "My Export");
// Make Deserialization: person = UjoManagerXML.getInstance().parseXML(inputStream, Person.class, "My Import");
<?xml version="1.0" encoding="UTF-8"?> <body> <NANE>Pavel Ponec</NAME> <MALE>true</MALE> <CASH>34.5</CASH> </body>
public class Person extends AbstractUjo { public static final Key <Person,String> NAME = newKey(); public static final ListProperty<Person,Child> CHILDS = newListKey(); }
public boolean readAuthorization(UjoAction action, Key property, Object value) { switch(action.getType()) { case ACTION_XML_EXPORT: return property!=NAME; default: { return super.readAuthorization(action, property, value); } } }Notice: because all the Keys of the UJO object are the final type, it is not necessary to compare by the equals() method, but it is possible to use a quicker operator ==.
@XmlAttribute public static final Key<Ujo,String> NAME = newKey();
@@XmlElementBody public static final Key<Ujo,String> MALE = newKey();A content of the new XML file using both annotations @XmlAttribute and @XmlElementBody is next:
<?xml version="1.0" encoding="UTF-8"?> <body Name="Pavel Ponec"> true <CASH>34.5</CASH> </body>Some more features:
// Create a model for all attributes: UjoTableModel<Person> model = new UjoTableModel(Person.NAME, Person.MALE, Person.BIRTH); // or simply: ... model = new UjoTableModel(Person.class) jTable.setModel(model);Other possibilities of the UjoTableModel class::
// We can set an data optionally: List<Person> persons = new ArrayList(); model.setRows(persons);
// Add a row: model.addRow(new Person());
// Set a value: model.setValueAt("Prokop", 0, Person.NAME );
// Get the last row of table: Person person = model.getRowLast();
// Sort the model: model.sort(Person.NAME); // or descending: model.sort(Person.NAME.descending());
final Reader reader = RingBuffer.createReader("xxx ${abc} ${def} zzz"); String word1 = RingBuffer.findWord(reader, "${", "}"); assertEquals("abc", word1); String word2 = RingBuffer.findWord(reader, "${", "}"); assertEquals("def", word2); String word3 = RingBuffer.findWord(reader, "${", "}"); assertEquals("", word3);
public class PersonExt extends Person { private static final KeyFactory<Person> f = newFactory(PersonExt.class); public static final Key<PersonExt,Float> WEIGHT = f.newKey(); public static final Key<PersonExt,Date > BORN = f.newKey(); @Override public KeyList<?> readKeys() { return f.getKeys(); } }Or the similar example without the class KeyFactory for a child of the class QuickUjo:
public class PersonExt extends Person { public static final Key<PersonExt,Float> WEIGHT = newKey(); public static final Key<PersonExt,Date > BORN = newKey();
}
Implementation | Performance [sec per 5M loops] |
Ratio [%] |
---|---|---|
ArrayUjo | 1.972 | 177 |
MapUjo | 6.651 | 597 |
BeanUjo | 20.995 | 1883 |
JavaBean | 1.115 | 100 |
The XML serialization of UJO objects is based on a quick SAX parser, performance tests are very favorable, mainly a comparison to XML serialization implemented in JER (classes XMLEncoder, XMLDecoder).
A measurement was provided on the same PC like the last test (UJO release 0.84), all results are shown in next table.Implementation | Time of serialization [sec] |
Time of deserialization [sec] |
Total ratio [%] |
---|---|---|---|
ArrayUjo | 0.332 | 0.305 | 93 |
MapUjo | 0.340 | 0.340 | 99 |
BeanUjo | 0.375 | 0.336 | 104 |
XMLEncoder | 2.797 | 1.770 | 668 |
JAXB | 0.211 | 0.473 | 100 |
ServletRequest request; UjoTextable person; Key personName; ... person.writeValueString(personName, request.getParameters(personName.getName());
Copyright 2007-2013 Pavel Ponec
Licensed 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.