This is an automated email from the git hooks/post-receive script. New commit to annotated tag v3.0-alpha-1 in repository nuiton-decorator. See https://gitlab.nuiton.org/nuiton/nuiton-decorator.git commit 890ab3130649e1bd2b02d483241e82ea87f84573 Author: Maven Release Manager <???> Date: Tue Jul 23 15:48:12 2013 +0000 [maven-release-plugin] copy for tag nuiton-decorator-3.0-alpha-1 --- LICENSE.txt | 166 +++++++++++ README.txt | 0 pom.xml | 177 ++++++++++++ src/main/java/org/nuiton/decorator/Decorator.java | 58 ++++ .../decorator/DecoratorMulti18nProvider.java | 318 +++++++++++++++++++++ .../org/nuiton/decorator/DecoratorProvider.java | 268 +++++++++++++++++ .../java/org/nuiton/decorator/DecoratorUtil.java | 299 +++++++++++++++++++ .../java/org/nuiton/decorator/JXPathDecorator.java | 262 +++++++++++++++++ .../org/nuiton/decorator/MapPropertyHandler.java | 179 ++++++++++++ .../org/nuiton/decorator/MultiJXPathDecorator.java | 124 ++++++++ .../org/nuiton/decorator/PropertyDecorator.java | 100 +++++++ .../java/org/nuiton/decorator/package-info.java | 71 +++++ src/site/apt/index.apt | 58 ++++ src/site/apt/versions.apt | 34 +++ src/site/site_fr.xml | 111 +++++++ src/test/java/org/nuiton/decorator/Data.java | 64 +++++ .../decorator/DecoratorMulti18nProviderTest.java | 228 +++++++++++++++ .../nuiton/decorator/DecoratorProviderTest.java | 175 ++++++++++++ .../org/nuiton/decorator/JXPathContextTester.java | 56 ++++ .../org/nuiton/decorator/JXPathDecoratorTest.java | 180 ++++++++++++ .../nuiton/decorator/MapPropertyHandlerTest.java | 127 ++++++++ .../nuiton/decorator/MultiJXPathDecoratorTest.java | 255 +++++++++++++++++ src/test/resources/log4j.properties | 34 +++ 23 files changed, 3344 insertions(+) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3f7b8b1 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5a9c74d --- /dev/null +++ b/pom.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>mavenpom4redmineAndCentral</artifactId> + <version>3.4.13</version> + </parent> + + <artifactId>nuiton-decorator</artifactId> + <version>3.0-alpha-1</version> + + <name>Nuiton Decorator</name> + <description>Simple Decorator API</description> + <url>http://maven-site.nuiton.org/nuiton-decorator</url> + <inceptionYear>2013</inceptionYear> + + <developers> + + <developer> + <name>Tony Chemit</name> + <id>tchemit</id> + <email>chemit at codelutin dot com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://www.codelutin.com/</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>developer</role> + </roles> + </developer> + + </developers> + + <scm> + <connection> + scm:svn:http://svn.nuiton.org/svn/nuiton-decorator/tags/nuiton-decorator-3.0-alpha-1 + </connection> + <developerConnection> + scm:svn:http://svn.nuiton.org/svn/nuiton-decorator/tags/nuiton-decorator-3.0-alpha-1 + </developerConnection> + <url>http://nuiton.org/projects/nuiton-decorator/repository/show/tags/nuiton-decorator-3.0-alpha-1</url> + </scm> + <distributionManagement> + <site> + <id>${platform}</id> + <url>${our.site.repository}/${projectId}</url> + </site> + </distributionManagement> + + <properties> + + <projectId>nuiton-decorator</projectId> + + <!-- Documentation is in apt format --> + <siteSourcesType>apt</siteSourcesType> + + <!-- extra files to include in release --> + <redmine.releaseFiles>${redmine.libReleaseFiles}</redmine.releaseFiles> + + </properties> + + <dependencies> + + <dependency> + <groupId>commons-jxpath</groupId> + <artifactId>commons-jxpath</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>provided</scope> + </dependency> + + </dependencies> + + <profiles> + + <profile> + <id>reporting</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <reporting> + <plugins> + + <plugin> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>${projectInfoReportsPluginVersion}</version> + <reportSets> + <reportSet> + <reports> + <report>project-team</report> + <report>mailing-list</report> + <report>cim</report> + <report>issue-tracking</report> + <report>license</report> + <report>scm</report> + <report>dependency-info</report> + <report>dependencies</report> + <report>dependency-convergence</report> + <report>plugin-management</report> + <report>plugins</report> + <report>dependency-management</report> + <report>summary</report> + </reports> + </reportSet> + </reportSets> + </plugin> + + </plugins> + </reporting> + + </profile> + + <!-- create assemblies at release time --> + <profile> + <id>assembly-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <defaultGoal>package</defaultGoal> + <plugins> + + <!-- launch in a release the assembly automaticly --> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>create-assemblies</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + <configuration> + <attach>false</attach> + <descriptorRefs> + <descriptorRef>deps</descriptorRef> + <descriptorRef>full</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + + </plugins> + + </build> + </profile> + + </profiles> +</project> diff --git a/src/main/java/org/nuiton/decorator/Decorator.java b/src/main/java/org/nuiton/decorator/Decorator.java new file mode 100644 index 0000000..c53907a --- /dev/null +++ b/src/main/java/org/nuiton/decorator/Decorator.java @@ -0,0 +1,58 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import java.io.Serializable; + +/** + * A simple contract to define a String decorator on any java object. + * + * @param <O> the type of data to decorate + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public abstract class Decorator<O> implements Serializable { + + private static final long serialVersionUID = -1L; + + /** Type of the data to decorate */ + protected final Class<O> type; + + public Decorator(Class<O> type) throws NullPointerException { + if (type == null) { + throw new NullPointerException("type can not be null."); + } + this.type = type; + } + + /** + * @param bean the bean to decorate + * @return the string value of the given bean + */ + public abstract String toString(Object bean); + + public Class<O> getType() { + return type; + } +} diff --git a/src/main/java/org/nuiton/decorator/DecoratorMulti18nProvider.java b/src/main/java/org/nuiton/decorator/DecoratorMulti18nProvider.java new file mode 100644 index 0000000..8c6646d --- /dev/null +++ b/src/main/java/org/nuiton/decorator/DecoratorMulti18nProvider.java @@ -0,0 +1,318 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * A decorator provider for multi-i18n locale. + * <p/> + * Implements the method {@link #loadDecorators(Locale)} to fill the decorators + * availables. + * <p/> + * Then can obtain decorator via the methods {@code getDecorator(...)} + * <p/> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public abstract class DecoratorMulti18nProvider { + + /** Logger */ + private static final Log log = LogFactory.getLog(DecoratorProvider.class); + + /** + * Loaded decorators. + * <p/> + * This map will be lazy loaded as needed via the method + * {@link #getDecoratorContexts(Locale, boolean)}. + */ + protected Map<Locale, Collection<DecoratorContext<?>>> decoratorContexts; + + + /** + * Load all decorators of the provider for the given {@code locale}. + * + * @param locale the locale to use to load decorators. + */ + protected abstract void loadDecorators(Locale locale); + + + /** + * Obtain a decorator for the given object using the given {@code locale}. + * + * @param locale user locale + * @param object object of decorated object + * @param <O> object of decorated object + * @return the decorator or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public <O> Decorator<O> getDecorator(Locale locale, O object) { + return getDecorator(locale, object, null); + } + + /** + * Obtain a decorator given a object and an extra name to qualify the + * context using the given {@code locale}. + * + * @param locale user locale + * @param object object of decorated object + * @param name extra name to qualify the decorator to use + * @param <O> object of decorated object + * @return the decorator or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public <O> Decorator<O> getDecorator(Locale locale, + O object, + String name) { + Class<O> k = (Class<O>) object.getClass(); + return getDecoratorByType(locale, k, name); + } + + /** + * Obtain a decorator given a type on the given {@code locale}. + * + * @param locale user locale + * @param type type of decorated object + * @param <O> type of decorated object + * @return the decorator or {@code null} if not found + */ + public <O> Decorator<O> getDecoratorByType(Locale locale, + Class<O> type) { + return getDecoratorByType(locale, type, null); + } + + /** + * Obtain a decorator given a type and a extra context name on the given + * {@code locale}. + * + * @param locale user locale + * @param type type of decorated object + * @param name extra name to qualify the decorator to use + * @param <O> type of decorated object + * @return the decorator or {@code null} if not found + */ + public <O> Decorator<O> getDecoratorByType(Locale locale, + Class<O> type, + String name) { + DecoratorContext<O> d = getDecoratorContext(locale, type, name, true); + return d == null ? null : d.getDecorator(); + } + + public void registerPropertyDecorator(Locale locale, + Class<?> klass, + String expression) { + registerPropertyDecorator(locale, klass, null, expression); + } + + public void registerJXPathDecorator(Locale locale, + Class<?> klass, String expression) { + registerJXPathDecorator(locale, klass, null, expression); + } + + public void registerMultiJXPathDecorator(Locale locale, + Class<?> klass, + String expression, + String separator, + String separatorReplacement) { + registerMultiJXPathDecorator(locale, klass, null, expression, separator, + separatorReplacement); + } + + public void registerPropertyDecorator(Locale locale, + Class<?> klass, + String name, + String expression) { + Decorator<?> decorator = + DecoratorUtil.newPropertyDecorator(klass, expression); + registerDecorator(locale, name, decorator); + } + + public void registerJXPathDecorator(Locale locale, + Class<?> klass, + String name, + String expression) { + Decorator<?> decorator = + DecoratorUtil.newJXPathDecorator(klass, expression); + registerDecorator(locale, name, decorator); + } + + public void registerMultiJXPathDecorator(Locale locale, + Class<?> klass, + String name, + String expression, + String separator, + String separatorReplacement) { + Decorator<?> decorator = DecoratorUtil.newMultiJXPathDecorator( + klass, expression, separator, separatorReplacement + ); + registerDecorator(locale, name, decorator); + } + + public void registerDecorator(Locale locale, + Decorator<?> decorator) { + registerDecorator(locale, null, decorator); + } + + /** + * Register a new decorator in the cache of the provider. + * + * @param <T> type of data decorated + * @param locale the given locale to use for this decorator + * @param context the name decorator + * @param decorator the decorator to register + */ + public <T> void registerDecorator(Locale locale, + String context, + Decorator<T> decorator) { + + // obtain the decorator context + DecoratorContext<?> result = + getDecoratorContext(locale, + decorator.getType(), + context, + false + ); + + if (result != null) { + throw new IllegalArgumentException( + "there is an already register decorator with context " + + result); + } + + DecoratorContext<T> decoratorContext = + new DecoratorContext<T>(context, decorator); + if (log.isDebugEnabled()) { + log.debug(decoratorContext); + } + getDecoratorContexts(locale, false).add(decoratorContext); + } + + public void clear() { + if (decoratorContexts != null) { + decoratorContexts.clear(); + } + } + + protected Map<Locale, Collection<DecoratorContext<?>>> getDecoratorContexts() { + if (decoratorContexts == null) { + decoratorContexts = new HashMap<Locale, Collection<DecoratorContext<?>>>(); + } + return decoratorContexts; + } + + protected Collection<DecoratorContext<?>> getDecoratorContexts(Locale locale, + boolean doLoad) { + + Collection<DecoratorContext<?>> decoratorContexts = + getDecoratorContexts().get(locale); + + if (decoratorContexts == null) { + decoratorContexts = new ArrayList<DecoratorContext<?>>(); + getDecoratorContexts().put(locale, decoratorContexts); + if (doLoad) { + loadDecorators(locale); + } + } + return decoratorContexts; + } + + @SuppressWarnings({"unchecked"}) + protected <T> DecoratorContext<T> getDecoratorContext(Locale locale, + Class<T> type, + String context, + boolean doLoad) { + DecoratorContext<T> result = null; + + Collection<DecoratorContext<?>> decoratorContexts = + getDecoratorContexts(locale, doLoad); + + if (decoratorContexts != null) { + for (DecoratorContext<?> d : decoratorContexts) { + if (type == null) { + if (d.accept(context)) { + result = (DecoratorContext<T>) d; + break; + } + continue; + } + if (d.accept(type, context)) { + result = (DecoratorContext<T>) d; + break; + } + } + } + return result; + } + + public static class DecoratorContext<T> { + + /** the context name of the decorator */ + final String context; + + /** the decorator */ + final Decorator<T> decorator; + + public DecoratorContext(String context, Decorator<T> decorator) { + this.context = context; + this.decorator = decorator; + } + + public String getContext() { + return context; + } + + public Decorator<T> getDecorator() { + return decorator; + } + + public Class<T> getType() { + return decorator.getType(); + } + + public boolean accept(Class<?> type, String context) { + boolean accept = getType().isAssignableFrom(type) && accept(context); + return accept; + } + + public boolean accept(String context) { + return this.context == null && context == null || + this.context != null && this.context.equals(context); + } + + @Override + public String toString() { + return super.toString() + "<type: " + getType().getName() + + ", context :" + context + ">"; + } + } +} diff --git a/src/main/java/org/nuiton/decorator/DecoratorProvider.java b/src/main/java/org/nuiton/decorator/DecoratorProvider.java new file mode 100644 index 0000000..98d2a48 --- /dev/null +++ b/src/main/java/org/nuiton/decorator/DecoratorProvider.java @@ -0,0 +1,268 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * A decorator provider. + * <p/> + * Implements the method {@link #loadDecorators()} to fill the decorators + * availables. + * <p/> + * Then can obtain decorator via the methods {@code getDecorator(...)} + * <p/> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public abstract class DecoratorProvider { + + /** Logger */ + private static final Log log = LogFactory.getLog(DecoratorProvider.class); + + /** Registred decorators. */ + protected List<DecoratorContext<?>> decorators; + + public DecoratorProvider() { + loadDecorators(); + } + + /** Load all decorators of the provider */ + protected abstract void loadDecorators(); + + /** + * Obtain a decorator for the given object. + * + * @param object object of decorated object + * @param <O> object of decorated object + * @return the decorator or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public <O> Decorator<O> getDecorator(O object) { + return getDecorator(object, null); + } + + /** + * Obtain a decorator given a object and an extra name to qualify the + * context. + * + * @param object object of decorated object + * @param name extra name to qualify the decorator to use + * @param <O> object of decorated object + * @return the decorator or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public <O> Decorator<O> getDecorator(O object, String name) { + Class<O> k = (Class<O>) object.getClass(); + return getDecoratorByType(k, name); + } + + /** + * Obtain a decorator given a type. + * + * @param type type of decorated object + * @param <O> type of decorated object + * @return the decorator or {@code null} if not found + */ + public <O> Decorator<O> getDecoratorByType(Class<O> type) { + return getDecoratorByType(type, null); + } + + /** + * Obtain a decorator given a type and a extra name. + * + * @param type type of decorated object + * @param name extra name to qualify the decorator to use + * @param <O> type of decorated object + * @return the decorator or {@code null} if not found + */ + public <O> Decorator<O> getDecoratorByType(Class<O> type, String name) { + DecoratorContext<O> d = getDecoratorContext(type, name); + return d == null ? null : d.getDecorator(); + } + + public void reload() { + clear(); + loadDecorators(); + } + + public void registerPropertyDecorator(Class<?> klass, + String expression) { + registerPropertyDecorator(klass, null, expression); + } + + public void registerJXPathDecorator(Class<?> klass, String expression) { + registerJXPathDecorator(klass, null, expression); + } + + public void registerMultiJXPathDecorator(Class<?> klass, + String expression, + String separator, + String separatorReplacement) { + registerMultiJXPathDecorator(klass, null, expression, separator, + separatorReplacement); + } + + public void registerPropertyDecorator(Class<?> klass, + String name, + String expression) { + Decorator<?> decorator = + DecoratorUtil.newPropertyDecorator(klass, expression); + registerDecorator(name, decorator); + } + + public void registerJXPathDecorator(Class<?> klass, + String name, + String expression) { + Decorator<?> decorator = + DecoratorUtil.newJXPathDecorator(klass, expression); + registerDecorator(name, decorator); + } + + public void registerMultiJXPathDecorator(Class<?> klass, + String name, + String expression, + String separator, + String separatorReplacement) { + Decorator<?> decorator = DecoratorUtil.newMultiJXPathDecorator( + klass, expression, separator, separatorReplacement + ); + registerDecorator(name, decorator); + } + + public void registerDecorator(Decorator<?> decorator) { + registerDecorator(null, decorator); + } + + /** + * Register a new decorator in the cache of the provider. + * + * @param <T> type of data decorated + * @param context the name decorator + * @param decorator the decorator to register + */ + public <T> void registerDecorator(String context, + Decorator<T> decorator) { + + // obtain the decorator context + DecoratorContext<?> result = + getDecoratorContext(decorator.getType(), context); + + if (result != null) { + throw new IllegalArgumentException( + "there is an already register decorator with context " + + result); + } + + DecoratorContext<T> decoratorContext = + new DecoratorContext<T>(context, decorator); + if (log.isDebugEnabled()) { + log.debug(decoratorContext); + } + getDecorators().add(decoratorContext); + } + + public void clear() { + if (decorators != null) { + decorators.clear(); + } + } + + protected List<DecoratorContext<?>> getDecorators() { + if (decorators == null) { + decorators = new ArrayList<DecoratorContext<?>>(); + } + return decorators; + } + + @SuppressWarnings({"unchecked"}) + protected <T> DecoratorContext<T> getDecoratorContext(Class<T> type, + String context) { + DecoratorContext<T> result = null; + if (decorators != null) { + for (DecoratorContext<?> d : decorators) { + if (type == null) { + if (d.accept(context)) { + result = (DecoratorContext<T>) d; + break; + } + continue; + } + if (d.accept(type, context)) { + result = (DecoratorContext<T>) d; + break; + } + } + } + return result; + } + + public static class DecoratorContext<T> { + + /** the context name of the decorator */ + final String context; + + /** the decorator */ + final Decorator<T> decorator; + + public DecoratorContext(String context, Decorator<T> decorator) { + this.context = context; + this.decorator = decorator; + } + + public String getContext() { + return context; + } + + public Decorator<T> getDecorator() { + return decorator; + } + + public Class<T> getType() { + return decorator.getType(); + } + + public boolean accept(Class<?> type, String context) { + boolean accept = getType().isAssignableFrom(type) && accept(context); + return accept; + } + + public boolean accept(String context) { + return this.context == null && context == null || + this.context != null && this.context.equals(context); + } + + @Override + public String toString() { + return super.toString() + "<type: " + getType().getName() + + ", context :" + context + ">"; + } + } + +} diff --git a/src/main/java/org/nuiton/decorator/DecoratorUtil.java b/src/main/java/org/nuiton/decorator/DecoratorUtil.java new file mode 100644 index 0000000..f5b7521 --- /dev/null +++ b/src/main/java/org/nuiton/decorator/DecoratorUtil.java @@ -0,0 +1,299 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.decorator.JXPathDecorator.Context; +import org.nuiton.decorator.JXPathDecorator.JXPathComparator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Some usefull methods on {@link Decorator} to create, and sort data with + * decorators. + * <p/> + * To create a new decorator, use one of the methods : <ul> <li>{@link + * #newPropertyDecorator(Class, String)}</li> <li>{@link + * #newJXPathDecorator(Class, String)}</li> <li> + * {@link #newMultiJXPathDecorator(Class, String, String)})</li> + * <li>{@link #newMultiJXPathDecorator(Class, String, String, String)})</li> + * </ul> + * <p/> + * To sort a list of data, using a {@link JXPathDecorator}, use the method + * {@link #sort(JXPathDecorator, List, int)}. + * <p/> + * + * @author tchemit <chemit@codelutiln.com> + * @since 2.3 + */ +public class DecoratorUtil { + + /** Logger */ + private static final Log log = LogFactory.getLog(DecoratorUtil.class); + + + /** + * Factory method to instanciate a new {@link PropertyDecorator} for the + * given class {@code internlaClass} and a readable property name. + * + * @param type the class of the objects decorated by the new + * decorator + * @param property the property + * @param <O> the generic type of class to be decorated by the new + * decorator + * @return the new instanciated decorator + * @throws IllegalArgumentException if the expression is not valid, says: + * <p/> + * - a missing right brace was detected. + * <p/> + * - a ${ was found in a jxpath token. + * @throws NullPointerException if type parameter is null. + */ + public static <O> PropertyDecorator<O> newPropertyDecorator( + Class<O> type, + String property) + throws IllegalArgumentException, NullPointerException { + return new PropertyDecorator<O>(type, property); + } + + /** + * Factory method to instanciate a new {@link JXPathDecorator} for the given + * class {@code internalClass} and expression. + * + * @param type the class of the objects decorated by the new + * decorator + * @param expression the expression to use to decorated objects + * @param <O> the generic type of class to be decorated by the new + * decorator + * @return the new instanciated decorator + * @throws IllegalArgumentException if the expression is not valid, says: + * <p/> + * - a missing right brace was detected. + * <p/> + * - a ${ was found in a jxpath token. + * @throws NullPointerException if type parameter is null. + */ + public static <O> JXPathDecorator<O> newJXPathDecorator( + Class<O> type, + String expression) + throws IllegalArgumentException, NullPointerException { + + Context<O> context = createJXPathContext(expression); + return new JXPathDecorator<O>(type, expression, context); + } + + public static <O> MultiJXPathDecorator<O> newMultiJXPathDecorator( + Class<O> type, + String expression, + String separator) + throws IllegalArgumentException, NullPointerException { + + MultiJXPathDecorator<O> decorator = newMultiJXPathDecorator( + type, + expression, + separator, + separator); + return decorator; + } + + public static <O> MultiJXPathDecorator<O> newMultiJXPathDecorator( + Class<O> type, + String expression, + String separator, + String separatorReplacement) + throws IllegalArgumentException, NullPointerException { + + Context<O>[] contexts = createMultiJXPathContext( + expression, + separator, + separatorReplacement); + + return new MultiJXPathDecorator<O>( + type, + expression, + separator, + separatorReplacement, + contexts); + } + + /** + * Sort a list of data based on the first token property of a given context + * in a given decorator. + * + * @param <O> type of data to sort + * @param decorator the decorator to use to sort + * @param datas the list of data to sort + * @param pos the index of context to used in decorator to obtain + * sorted property. + */ + public static <O> void sort(JXPathDecorator<O> decorator, + List<O> datas, + int pos) { + sort(decorator, datas, pos, false); + } + + /** + * Sort a list of data based on the first token property of a given context + * in a given decorator. + * + * @param <O> type of data to sort + * @param decorator the decorator to use to sort + * @param datas the list of data to sort + * @param pos the index of context to used in decorator to obtain + * sorted property. + * @param reverse flag to sort in reverse order if sets to {@code true} + * @since 2.2 + */ + public static <O> void sort(JXPathDecorator<O> decorator, + List<O> datas, + int pos, + boolean reverse) { + Comparator<O> c = null; + boolean cachedComparator = false; + try { + c = decorator.getComparator(pos); + cachedComparator = c instanceof JXPathComparator<?>; + + if (cachedComparator) { + ((JXPathComparator<O>) c).init(decorator, datas); + } + Collections.sort(datas, c); + if (reverse) { + + // reverse order + Collections.reverse(datas); + } + } finally { + if (cachedComparator) { + ((JXPathComparator<?>) c).clear(); + } + } + } + + public static <O> Context<O> createJXPathContext(String expression) { + List<String> lTokens = new ArrayList<String>(); + StringBuilder buffer = new StringBuilder(); + int size = expression.length(); + int end = -1; + int start; + while ((start = expression.indexOf("${", end + 1)) > -1) { + if (start > end + 1) { + + // prefix of next jxpath token + buffer.append(expression.substring(end + 1, start)); + } + + // seek end of jxpath + end = expression.indexOf("}", start + 1); + if (end == -1) { + throw new IllegalArgumentException( + "could not find the rigth brace starting at car " + + start + " : " + expression.substring(start + 2)); + } + String jxpath = expression.substring(start + 2, end); + + // not allowed ${ inside a jxpath token + if (jxpath.contains("${")) { + throw new IllegalArgumentException( + "could not find a ${ inside a jxpath expression at " + + "car " + (start + 2) + " : " + jxpath); + } + + // save the jxpath token + lTokens.add(jxpath); + + // replace jxpath token in expresion with a string format variable + buffer.append('%').append(lTokens.size()); + } + if (size > end + 1) { + + // suffix after end jxpath (or all expression if no jxpath) + buffer.append(expression.substring(end + 1)); + } + String[] tokens = lTokens.toArray(new String[lTokens.size()]); + return new Context<O>(buffer.toString(), tokens); + } + + public static <O> Context<O>[] createMultiJXPathContext( + String expression, + String separator, + String separatorReplacement) { + int sep = expression.indexOf(separator); + if (sep == -1) { + Context<O>[] result = newInstance(1); + result[0] = createJXPathContext(expression); + return result; + } + + List<String> tokens = new ArrayList<String>(); + StringTokenizer stk = new StringTokenizer(expression, separator); + while (stk.hasMoreTokens()) { + tokens.add(stk.nextToken()); + } + + int nbTokens = tokens.size(); + Context<O>[] contexts = newInstance(nbTokens); + if (log.isDebugEnabled()) { + log.debug("Will prepare " + nbTokens + " contexts from [" + expression + "]"); + } + for (int i = 0; i < nbTokens; i++) { + StringBuilder buffer = new StringBuilder(expression.length()); + for (int j = 0; j < nbTokens; j++) { + int index = (i + j) % nbTokens; + String str = tokens.get(index); + + //replace all '%(index+1)$' pattern with '%(j+1)$' + Pattern p = Pattern.compile("\\%(" + (index + 1) + ")\\$"); + Matcher matcher = p.matcher(str); + String safeStr = matcher.replaceAll("\\%" + (j + 1) + "\\$"); + + if (log.isDebugEnabled()) { + log.debug("[" + (index + 1) + "-->" + (j + 1) + "] " + str + + " transformed to " + safeStr); + } + buffer.append(separatorReplacement).append(safeStr); + } + String expr = buffer.substring(separatorReplacement.length()); + if (log.isDebugEnabled()) { + log.debug("context [" + i + "] : " + expr); + } + contexts[i] = createJXPathContext( + expr); + } + return contexts; + } + + @SuppressWarnings("unchecked") + protected static <O> Context<O>[] newInstance(int size) { + // fixme how to instanciate a typed array with no checking warning ? + return new Context[size]; + } +} diff --git a/src/main/java/org/nuiton/decorator/JXPathDecorator.java b/src/main/java/org/nuiton/decorator/JXPathDecorator.java new file mode 100644 index 0000000..fca7e81 --- /dev/null +++ b/src/main/java/org/nuiton/decorator/JXPathDecorator.java @@ -0,0 +1,262 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.jxpath.JXPathContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * JXPath decorator based on {@link String#format(String, Object...)} method. + * <p/> + * To use it, give to him a expression where all jxpath to apply on bean are + * boxed in <code>${}</code>. + * <p/> + * After the jxpath token you must specifiy the formatter to apply of the + * jxpath token. + * <p/> + * For example : + * <pre> + * Decorator<Object> d = DecoratorUtil.newJXPathDecorator( + * JXPathDecorator.class,"expr = ${expressions}$s"); + * assert "expr = %1$s" == d.getExpression(); + * assert 1 == d.getNbToken(); + * assert java.util.Arrays.asList("expression") == d.getTokens(); + * assert "expr = %1$s" == d.toString(d); + * </pre> + * + * @param <O> type of data to decorate + * @author tchemit <chemit@codelutin.com> + * @see Decorator + * @since 2.3 + */ +public class JXPathDecorator<O> extends Decorator<O> { + + private static final long serialVersionUID = 1L; + + /** Logger */ + private static final Log log = LogFactory.getLog(JXPathDecorator.class); + + /** the computed context of the decorator */ + protected Context<O> context; + + /** nb jxpath tokens to compute */ + protected int nbToken; + + /** the initial expression used to compute the decorator context. */ + protected String initialExpression; + + protected JXPathDecorator(Class<O> internalClass, + String expression, + Context<O> context) + throws IllegalArgumentException, NullPointerException { + super(internalClass); + initialExpression = expression; + if (context != null) { + setContext(context); + } + } + + @Override + public String toString(Object bean) { + if (bean == null) { + return null; + } + JXPathContext jxcontext = JXPathContext.newContext(bean); + Object[] args = new Object[nbToken]; + + for (int i = 0; i < nbToken; i++) { + try { + args[i] = getTokenValue(jxcontext, context.tokens[i]); + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error("can not obtain token " + context.tokens[i] + + " on object of type " + + bean.getClass().getName() + + " for reason " + e.getMessage(), e); + } + + } + } + + String result; + try { + result = String.format(context.expression, args); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Could not format " + context.expression + "" + + " with args : " + Arrays.toString(args), eee); + } + result = ""; + } + return result; + } + + public String getProperty(int pos) { + return getTokens()[pos]; + } + + public String getExpression() { + return context.expression; + } + + public String[] getTokens() { + return context.tokens; + } + + public int getNbToken() { + return nbToken; + } + + public String getInitialExpression() { + return initialExpression; + } + + @Override + public String toString() { + return super.toString() + '<' + context + '>'; + } + + public void setContext(Context<O> context) { + this.context = context; + nbToken = context.tokens.length; + // always reset comparator + //this.context.comparator = null; + if (log.isDebugEnabled()) { + log.debug(context); + } + } + + @SuppressWarnings({"unchecked"}) + protected Comparator<O> getComparator(int pos) { + ensureTokenIndex(this, pos); + return context.getComparator(pos); + } + + @SuppressWarnings({"unchecked"}) + protected Comparable<Comparable<?>> getTokenValue(JXPathContext jxcontext, + String token) { + // assume all values are comparable + return (Comparable<Comparable<?>>) jxcontext.getValue(token); + } + + public static class JXPathComparator<O> implements Comparator<O> { + + protected Map<O, Comparable<Comparable<?>>> valueCache; + + private final String expression; + + public JXPathComparator(String expression) { + this.expression = expression; + valueCache = new HashMap<O, Comparable<Comparable<?>>>(); + } + + @Override + public int compare(O o1, O o2) { + Comparable<Comparable<?>> c1 = valueCache.get(o1); + Comparable<Comparable<?>> c2 = valueCache.get(o2); + if (c1 == null) { + if (c2 == null) { + return 0; + } + return 1; + } + if (c2 == null) { + return -1; + } + return c1.compareTo(c2); + } + + public void clear() { + valueCache.clear(); + } + + public void init(JXPathDecorator<O> decorator, List<O> datas) { + clear(); + for (O data : datas) { + JXPathContext jxcontext = JXPathContext.newContext(data); + Comparable<Comparable<?>> key; + key = decorator.getTokenValue(jxcontext, expression); + valueCache.put(data, key); + } + } + } + + public static class Context<O> implements Serializable { + + /** + * expression to format using {@link String#format(String, Object...)}, + * all variables are compute using using the jxpath tokens. + */ + protected String expression; + + /** list of jxpath tokens to apply on expression */ + protected String[] tokens; + + protected transient Comparator<O> comparator; + + private static final long serialVersionUID = 1L; + + public Context(String expression, String[] tokens) { + this.expression = expression; + this.tokens = tokens; + } + + public String getFirstProperty() { + return tokens[0]; + } + + public Comparator<O> getComparator(int pos) { + if (comparator == null) { + comparator = new JXPathComparator<O>(tokens[pos]); + } + return comparator; + } + + public void setComparator(Comparator<O> comparator) { + this.comparator = comparator; + } + + @Override + public String toString() { + return "<expression:" + expression + ", tokens:" + + Arrays.toString(tokens) + '>'; + } + } + + protected static void ensureTokenIndex(JXPathDecorator<?> decorator, int pos) { + if (pos < -1 || pos > decorator.getNbToken()) { + throw new ArrayIndexOutOfBoundsException( + "token index " + pos + " is out of bound, can be inside [" + + 0 + ',' + decorator.nbToken + ']'); + } + } +} diff --git a/src/main/java/org/nuiton/decorator/MapPropertyHandler.java b/src/main/java/org/nuiton/decorator/MapPropertyHandler.java new file mode 100644 index 0000000..e4b016a --- /dev/null +++ b/src/main/java/org/nuiton/decorator/MapPropertyHandler.java @@ -0,0 +1,179 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.jxpath.DynamicPropertyHandler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A extended handler to deal with map in JXPath contexts. + * <p/> + * The basic one in JXPath api only deals with map keys as string. + * <p/> + * We offers a hook in method {@link #getKey(Object)} to obtain a string + * representation of a real object in map (key or value). + * <p/> + * More over, you can also access directly to a key or a value, using this + * syntax : + * <p/> + * <pre>context.getValue(".[@name='key:programme2']")</pre> + * <pre>context.getValue(".[@name='value:programme2']")</pre> + * <p/> + * If the values are iterable, then will scan inot it when looking for a direct + * value. + * + * @author tchemit <chemit@codelutin.com> + * @see DynamicPropertyHandler + * @since 2.3 + */ +public class MapPropertyHandler implements DynamicPropertyHandler { + + /** Logger */ + private static final Log log = + LogFactory.getLog(MapPropertyHandler.class); + + @Override + public String[] getPropertyNames(Object object) { + Map<?, ?> map = (Map<?, ?>) object; + Set<?> set = map.keySet(); + String[] names = new String[set.size()]; + Iterator<?> it = set.iterator(); + for (int i = 0; i < names.length; i++) { + Object o = it.next(); + names[i] = getKey(o); + } + return names; + } + + @Override + public Object getProperty(Object object, String propertyName) { + Map<?, ?> map = (Map<?, ?>) object; + boolean getKey = false; + Object property0; + if (propertyName.startsWith("value:")) { + propertyName = propertyName.substring(6); + if (log.isDebugEnabled()) { + log.debug("property value name " + propertyName); + } + property0 = getPropertyValue(map, propertyName); + if (log.isDebugEnabled()) { + log.debug("property value = " + property0); + } + return property0; + } + if (propertyName.startsWith("key:")) { + propertyName = propertyName.substring(4); + getKey = true; + } + property0 = getPropertyKey(map, propertyName); + Object result = null; + if (property0 != null) { + if (getKey) { + result = property0; + } else { + result = map.get(property0); + } + } + return result; + } + + @Override + public void setProperty(Object object, String propertyName, Object value) { + Map map = (Map) object; + Object property0 = getPropertyKey(map, propertyName); + if (property0 != null) { + map.put(property0, value); + } + } + + /** + * Obtain the key from the map keys which matches the given {@code key}. + * <p/> + * To compare object ot string, please refers to the method {@link + * #getKey(Object)}. + * + * @param map the map to scan + * @param key the string representation of the required key as object + * @return the found key, or {@code null} if not found. + */ + public Object getPropertyKey(Map<?, ?> map, String key) { + Set<?> set = map.keySet(); + for (Object o : set) { + String k = getKey(o); + if (key.equals(k)) { + return o; + } + } + return null; + } + + /** + * Obtain the value from the map values which matches the given {@code + * value}. + * <p/> + * To compare object to string, please refer to the method {@link + * #getKey(Object)}. + * + * @param map the map to scan + * @param value the string representation of the value + * @return the found value, or {@code null} if not found.} + */ + public Object getPropertyValue(Map<?, ?> map, String value) { + Collection<?> set = map.values(); + for (Object o : set) { + if (o instanceof Iterable<?>) { + Iterable<?> c = (Iterable<?>) o; + for (Object oo : c) { + String k = getKey(oo); + if (value.equals(k)) { + return oo; + } + } + continue; + } + String k = getKey(o); + if (value.equals(k)) { + return o; + } + } + return null; + } + + /** + * Obtain a string representation of an object. + * + * @param o the object to decorate + * @return the string representation of the object + */ + protected String getKey(Object o) { + String k = String.valueOf(o); + return k; + } +} diff --git a/src/main/java/org/nuiton/decorator/MultiJXPathDecorator.java b/src/main/java/org/nuiton/decorator/MultiJXPathDecorator.java new file mode 100644 index 0000000..ccabfff --- /dev/null +++ b/src/main/java/org/nuiton/decorator/MultiJXPathDecorator.java @@ -0,0 +1,124 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Comparator; + +/** + * {@link JXPathDecorator} implementation with multiple contexts. + * + * @param <O> type of data to decorate + * @author tchemit <chemit@codelutin.com> + * @see Decorator + * @since 2.3 + */ +public class MultiJXPathDecorator<O> extends JXPathDecorator<O> { + + private static final long serialVersionUID = 1L; + + /** Logger */ + private static final Log log = + LogFactory.getLog(MultiJXPathDecorator.class); + + /** Contexts of the decorator */ + protected Context<O>[] contexts; + + /** context separator */ + protected String separator; + + /** context separator replacement */ + protected String separatorReplacement; + + protected MultiJXPathDecorator( + Class<O> internalClass, + String expression, + String separator, + String separatorReplacement, + Context<O>[] contexts) throws IllegalArgumentException, + NullPointerException { + super(internalClass, expression, null); + this.separator = separator; + this.separatorReplacement = separatorReplacement; + this.contexts = contexts; + + setContextIndex(0); + + if (log.isDebugEnabled()) { + log.debug(expression + " --> " + context); + } + } + + protected MultiJXPathDecorator( + Class<O> internalClass, + String expression, + String separator, + String separatorReplacement) throws IllegalArgumentException, + NullPointerException { + this(internalClass, + expression, + separator, + separatorReplacement, + DecoratorUtil.<O>createMultiJXPathContext(expression, + separator, + separatorReplacement) + ); + } + + public void setContextIndex(int index) { + ensureContextIndex(this, index); + setContext(contexts[index]); + } + + public int getNbContext() { + return contexts.length; + } + + public String getSeparator() { + return separator; + } + + public String getSeparatorReplacement() { + return separatorReplacement; + } + + @Override + protected Comparator<O> getComparator(int pos) { + ensureContextIndex(this, pos); + Context<O> context1 = contexts[pos]; + return context1.getComparator(0); + } + + protected void ensureContextIndex(MultiJXPathDecorator<?> decorator, + int pos) { + if (pos < -1 || pos > decorator.contexts.length) { + throw new ArrayIndexOutOfBoundsException( + "context index " + pos + + " is out of bound, can be inside [" + 0 + "," + + decorator.contexts.length + "]"); + } + } +} diff --git a/src/main/java/org/nuiton/decorator/PropertyDecorator.java b/src/main/java/org/nuiton/decorator/PropertyDecorator.java new file mode 100644 index 0000000..384998e --- /dev/null +++ b/src/main/java/org/nuiton/decorator/PropertyDecorator.java @@ -0,0 +1,100 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; + +/** + * Simple property decorator based on {@link String#format(String, Object...)} + * method. + * <p/> + * To use it, give him a class and the property name to render. + * <p/> + * For example : + * <pre> + * Decorator<Object> d = DecoratorUtil.newPropertyDecorator(PropertyDecorator.class,"property"); + * </pre> + * + * @param <O> type of data to decorate + * @author tchemit <chemit@codelutin.com> + * @see Decorator + * @since 2.3 + */ +public class PropertyDecorator<O> extends Decorator<O> { + + private static final long serialVersionUID = 1L; + + /** Logger */ + static private final Log log = LogFactory.getLog(PropertyDecorator.class); + + /** name of property */ + protected String property; + + protected transient Method m; + + @Override + public String toString(Object bean) { + try { + return getM().invoke(bean) + ""; + } catch (Exception e) { + log.error("could not convert for reason : " + e, e); + return ""; + } + } + + public String getProperty() { + return property; + } + + protected PropertyDecorator(Class<O> internalClass, + String property) throws NullPointerException { + super(internalClass); + if (property == null) { + throw new NullPointerException("property can not be null."); + } + this.property = property; + // init method + getM(); + } + + protected Method getM() { + if (m == null) { + for (PropertyDescriptor propertyDescriptor : PropertyUtils.getPropertyDescriptors(getType())) { + if (propertyDescriptor.getName().equals(property)) { + m = propertyDescriptor.getReadMethod(); + break; + } + } + if (m == null) { + throw new IllegalArgumentException("could not find the property " + property + " in " + getType()); + } + } + return m; + } +} diff --git a/src/main/java/org/nuiton/decorator/package-info.java b/src/main/java/org/nuiton/decorator/package-info.java new file mode 100644 index 0000000..964392a --- /dev/null +++ b/src/main/java/org/nuiton/decorator/package-info.java @@ -0,0 +1,71 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * + * This package contains a api to do some decoration over any object. + * + * <h1>Decorator api</h1> + * The {@link org.nuiton.decorator.Decorator} has an internal state which + * is the type of object to decorate and a method to do the decoration : + * {@link org.nuiton.decorator.Decorator#toString(Object)}. + * <p/> + * There is some default implementations of a decorator using a simple property, + * or more complex one using jxpath mecanisms. + * + * <h1>DecoratorProvider api</h1> + * The {@link org.nuiton.decorator.DecoratorProvider} api is there to + * provide some decorators for you, + * + * There is a second one {@link org.nuiton.decorator.DecoratorMulti18nProvider} + * which take account of a given locale (so can be used in web context for + * example). + * + * User has to fill method + * {@link org.nuiton.decorator.DecoratorProvider}#loadDecorators()} + * or + * {@link org.nuiton.decorator.DecoratorMulti18nProvider#loadDecorators(Locale)} + * to specify which decorators are available. + * + * Then it can use the provider using the methods {@code getDecorator(XXX)} + * + * <strong>Note:</strong> Decorator can be contextualized so for a same type of + * object we can provide different way of decorating then. + * + * <h1>DecoratorUtil class</h1> + * + * This class offers some usefull method to create new decorator via the + * {@code newXXX} methods and also some method to sort list of objects using a + * decorator. + * + * <strong>Note: </strong> This api comes from the jaxx-runtime project. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + * @see org.nuiton.decorator.Decorator + * @see org.nuiton.decorator.DecoratorProvider + * @see org.nuiton.decorator.DecoratorMulti18nProvider + */ +package org.nuiton.decorator; + +import java.util.Locale; diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt new file mode 100644 index 0000000..3b7665b --- /dev/null +++ b/src/site/apt/index.apt @@ -0,0 +1,58 @@ +~~~ +~~ #%L +~~ Nuiton Decorator +~~ $Id$ +~~ $HeadURL$ +~~ %% +~~ Copyright (C) 2004 - 2010 CodeLutin +~~ %% +~~ This program is free software: you can redistribute it and/or modify +~~ it under the terms of the GNU Lesser General Public License as +~~ published by the Free Software Foundation, either version 3 of the +~~ License, or (at your option) any later version. +~~ +~~ This program is distributed in the hope that it will be useful, +~~ but WITHOUT ANY WARRANTY; without even the implied warranty of +~~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +~~ GNU General Lesser Public License for more details. +~~ +~~ You should have received a copy of the GNU General Lesser Public +~~ License along with this program. If not, see +~~ <http://www.gnu.org/licenses/lgpl-3.0.html>. +~~ #L% +~~~ + ---- + Nuiton decorator + ---- + ---- + 2013-08-20 + ---- + + +Présentation + + API simple décoration. + + +Note + + <<Nuiton-decorator>> quitte le projet <nuiton-utils> pour devenir un projet autonome. + + Voici quelques liens sur le nouveau projet: + + * {{{http://svn.nuiton.org/svn/nuiton-decorator}svn}} + + * {{{http://nuiton.org/projects/nuiton-decorator}forge}} + + * {{{http://maven-site.nuiton.org/nuiton-decorator}site}} + + [] + + Il faut alors utiliser cet artefact (<org.nuiton:nuiton-decorator>). + + La dernière version stable dans nuiton-utils est la 2.7; vous pouvez dès à + présent utiliser la version 3.0-alpha-1 de nuiton-csv. + + Pour plus de détails sur les changements importants entre chaque version, + vous pouvez consulter les {{{./versions.html}Notes de versions}}. + diff --git a/src/site/apt/versions.apt b/src/site/apt/versions.apt new file mode 100644 index 0000000..a8c1cda --- /dev/null +++ b/src/site/apt/versions.apt @@ -0,0 +1,34 @@ +~~~ +~~ #%L +~~ Nuiton Config +~~ $Id$ +~~ $HeadURL$ +~~ %% +~~ Copyright (C) 2013 CodeLutin +~~ %% +~~ This program is free software: you can redistribute it and/or modify +~~ it under the terms of the GNU Lesser General Public License as +~~ published by the Free Software Foundation, either version 3 of the +~~ License, or (at your option) any later version. +~~ +~~ This program is distributed in the hope that it will be useful, +~~ but WITHOUT ANY WARRANTY; without even the implied warranty of +~~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +~~ GNU General Lesser Public License for more details. +~~ +~~ You should have received a copy of the GNU General Lesser Public +~~ License along with this program. If not, see +~~ <http://www.gnu.org/licenses/lgpl-3.0.html>. +~~ #L% +~~~ + ---- + Nuiton decorator + ---- + ---- + 2013-07-23 + ---- + +Utilisation de la version 3.0 + + * Pour passer sur cette version, il faut changer les packages <org.nuiton.util.decorator> + en <org.nuiton.decorator>. \ No newline at end of file diff --git a/src/site/site_fr.xml b/src/site/site_fr.xml new file mode 100644 index 0000000..d67f037 --- /dev/null +++ b/src/site/site_fr.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + Nuiton Decorator + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + + +<project name="${project.name}"> + + <skin> + <groupId>org.apache.maven.skins</groupId> + <artifactId>maven-fluido-skin</artifactId> + <version>1.3.0</version> + </skin> + + <custom> + <fluidoSkin> + <topBarEnabled>false</topBarEnabled> + <googleSearch/> + <sideBarEnabled>true</sideBarEnabled> + <searchEnabled>true</searchEnabled> + <sourceLineNumbersEnabled>true</sourceLineNumbersEnabled> + </fluidoSkin> + </custom> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <bannerRight> + <src>http://www.codelutin.com/images/lutinorange-codelutin.png</src> + <href>http://www.codelutin.com</href> + </bannerRight> + + <publishDate position="right" /> + <version position="right" /> + + <poweredBy> + + <logo href="http://maven.apache.org" name="Maven" + img="http://maven-site.chorem.org/public/images/logos/maven-feather.png"/> + + </poweredBy> + + <body> + + <head> + <script type="text/javascript" + src="http://maven-site.chorem.org/public/js/mavenpom-site.js"> + </script> + + <link rel="stylesheet" type="text/css" + href="http://maven-site.chorem.org/public/css/mavenpom-site.css"/> + </head> + + <links> + <item name="Nuiton.org" href="http://nuiton.org"/> + <item name="Code Lutin" href="http://www.codelutin.com"/> + <item name="Libre entreprise" href="http://www.libre-entreprise.org"/> + </links> + + <breadcrumbs> + <item name="${project.name}" + href="${project.url}/index.html"/> + </breadcrumbs> + + <menu ref="modules"/> + + <menu name="Utilisateur"> + <item name="Accueil" href="index.html"/> + <item name="Note de versions" href="versions.html"/> + </menu> + + <menu ref="reports"/> + + <footer> + + <div id='projectMetas' + projectversion='${project.version}' + platform='${project.platform}' + projectid='${project.projectId}' + scm='${project.scm.developerConnection}' + scmwebeditorenabled='${project.scmwebeditorEnabled}' + scmwebeditorurl='${project.scmwebeditorUrl}' + siteSourcesType='${project.siteSourcesType}' + piwikEnabled='${project.piwikEnabled}' + piwikId='${project.piwikId}' locale='fr'> + </div> + </footer> + </body> +</project> diff --git a/src/test/java/org/nuiton/decorator/Data.java b/src/test/java/org/nuiton/decorator/Data.java new file mode 100644 index 0000000..8acf33f --- /dev/null +++ b/src/test/java/org/nuiton/decorator/Data.java @@ -0,0 +1,64 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class Data { + + int pos; + + String name; + + protected static List<Data> generate(int nb) { + List<Data> datas = new ArrayList<Data>(nb); + for (int i = 0; i < nb; i++) { + datas.add(new Data(i, "name_" + (nb - i))); + } + return datas; + } + + Data(int pos, String name) { + this.pos = pos; + this.name = name; + } + + public int getPos() { + return pos; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "Data{pos=" + pos + ", name=\'" + name + '\'' + '}'; + } +} diff --git a/src/test/java/org/nuiton/decorator/DecoratorMulti18nProviderTest.java b/src/test/java/org/nuiton/decorator/DecoratorMulti18nProviderTest.java new file mode 100644 index 0000000..a0a642d --- /dev/null +++ b/src/test/java/org/nuiton/decorator/DecoratorMulti18nProviderTest.java @@ -0,0 +1,228 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Locale; + +/** + * Tests the {@link DecoratorMulti18nProvider} + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class DecoratorMulti18nProviderTest { + + private static final String BY_NAME = "name"; + + private static final String BY_POS = "pos"; + + static protected Locale l_fr = Locale.FRANCE; + + static protected Locale l_en = Locale.UK; + + class MyDecoratorProvider extends DecoratorMulti18nProvider { + + @Override + protected void loadDecorators(Locale locale) { + + registerPropertyDecorator(locale, File.class, "name"); + registerPropertyDecorator(locale, File.class, BY_NAME, "parent"); + + registerJXPathDecorator(locale, Class.class, "${simpleName}$s " + locale.getCountry()); + registerJXPathDecorator(locale, Class.class, BY_NAME, "${name}$s " + locale.getCountry()); + + registerMultiJXPathDecorator(locale, Data.class, "${name}$s " + locale.getCountry(), "-", " "); + registerMultiJXPathDecorator(locale, Data.class, BY_POS, "${pos}$d " + locale.getCountry(), "-", " "); + } + } + + MyDecoratorProvider provider; + + @Before + public void beforeTest() throws Exception { + provider = new MyDecoratorProvider(); + } + + @Test + public void testGetDecoratorByObject() throws Exception { + + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecorator(l_fr, f); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("myFile", fileDecorator.toString(f)); + + Decorator<?> classDecorator; + + classDecorator = provider.getDecorator(l_fr, Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File FR", classDecorator.toString(k)); + + classDecorator = provider.getDecorator(l_en, Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File GB", classDecorator.toString(k)); + + Decorator<Data> dataDecorator; + + dataDecorator = provider.getDecorator(l_fr, d); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name FR", dataDecorator.toString(d)); + + dataDecorator = provider.getDecorator(l_en, d); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name GB", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByObjectAndName() throws Exception { + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecorator(l_fr, f, BY_NAME); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("null", fileDecorator.toString(f)); + + Decorator<Class> classDecorator; + + classDecorator = provider.getDecoratorByType(l_fr, Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File FR", classDecorator.toString(k)); + + classDecorator = provider.getDecoratorByType(l_en, Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File GB", classDecorator.toString(k)); + + + Decorator<Data> dataDecorator; + + dataDecorator = provider.getDecorator(l_fr, d, BY_NAME); + Assert.assertNull(dataDecorator); + + dataDecorator = provider.getDecorator(l_en, d, BY_NAME); + Assert.assertNull(dataDecorator); + + dataDecorator = provider.getDecorator(l_fr, d, BY_POS); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0 FR", dataDecorator.toString(d)); + + dataDecorator = provider.getDecorator(l_en, d, BY_POS); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0 GB", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByType() throws Exception { + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecoratorByType(l_fr, File.class); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("myFile", fileDecorator.toString(f)); + + Decorator<Class> classDecorator; + + classDecorator = provider.getDecoratorByType(l_fr, Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File FR", classDecorator.toString(k)); + + classDecorator = provider.getDecoratorByType(l_en, Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File GB", classDecorator.toString(k)); + + Decorator<Data> dataDecorator; + + dataDecorator = provider.getDecoratorByType(l_fr, Data.class); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name FR", dataDecorator.toString(d)); + + dataDecorator = provider.getDecoratorByType(l_en, Data.class); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name GB", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByTypeAndName() throws Exception { + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecoratorByType(l_fr, File.class, BY_NAME); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("null", fileDecorator.toString(f)); + + Decorator<Class> classDecorator; + + classDecorator = provider.getDecoratorByType(l_fr, Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File FR", classDecorator.toString(k)); + + classDecorator = provider.getDecoratorByType(l_en, Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File GB", classDecorator.toString(k)); + + Decorator<Data> dataDecorator; + + dataDecorator = provider.getDecoratorByType(l_fr, Data.class, BY_POS); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0 FR", dataDecorator.toString(d)); + + dataDecorator = provider.getDecoratorByType(l_en, Data.class, BY_POS); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0 GB", dataDecorator.toString(d)); + } + + +} diff --git a/src/test/java/org/nuiton/decorator/DecoratorProviderTest.java b/src/test/java/org/nuiton/decorator/DecoratorProviderTest.java new file mode 100644 index 0000000..1352f60 --- /dev/null +++ b/src/test/java/org/nuiton/decorator/DecoratorProviderTest.java @@ -0,0 +1,175 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class DecoratorProviderTest { + + private static final String BY_NAME = "name"; + + class MyDecoratorProvider extends DecoratorProvider { + + @Override + protected void loadDecorators() { + + registerPropertyDecorator(File.class, "name"); + registerPropertyDecorator(File.class, BY_NAME, "parent"); + + registerJXPathDecorator(Class.class, "${simpleName}$s"); + registerJXPathDecorator(Class.class, BY_NAME, "${name}$s"); + + registerMultiJXPathDecorator(Data.class, "${name}$s", "-", " "); + registerMultiJXPathDecorator(Data.class, BY_NAME, "${pos}$d", "-", " "); + } + } + + DecoratorProvider provider; + + @Before + public void beforeTest() throws Exception { + provider = new MyDecoratorProvider(); + } + + @Test + public void testGetDecoratorByObject() throws Exception { + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecorator(f); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("myFile", fileDecorator.toString(f)); + + Decorator<?> classDecorator = provider.getDecorator(Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File", classDecorator.toString(k)); + + Decorator<Data> dataDecorator = provider.getDecorator(d); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByObjectAndName() throws Exception { + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecorator(f, BY_NAME); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("null", fileDecorator.toString(f)); + + Decorator<Class> classDecorator = provider.getDecoratorByType(Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File", classDecorator.toString(k)); + + + Decorator<Data> dataDecorator = provider.getDecorator(d, BY_NAME); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByType() throws Exception { + + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecoratorByType(File.class); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("myFile", fileDecorator.toString(f)); + + Decorator<Class> classDecorator = provider.getDecoratorByType(Class.class); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("File", classDecorator.toString(k)); + + Decorator<Data> dataDecorator = provider.getDecoratorByType(Data.class); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("name", dataDecorator.toString(d)); + } + + @Test + public void testGetDecoratorByTypeAndName() throws Exception { + File f = new File("myFile"); + Data d = new Data(0, "name"); + Class<?> k = File.class; + + Decorator<File> fileDecorator = provider.getDecoratorByType(File.class, BY_NAME); + Assert.assertNotNull(fileDecorator); + Assert.assertEquals(File.class, fileDecorator.getType()); + Assert.assertEquals("null", fileDecorator.toString(f)); + + Decorator<Class> classDecorator = provider.getDecoratorByType(Class.class, BY_NAME); + Assert.assertNotNull(classDecorator); + Assert.assertEquals(Class.class, classDecorator.getType()); + Assert.assertEquals("java.io.File", classDecorator.toString(k)); + + + Decorator<Data> dataDecorator = provider.getDecoratorByType(Data.class, BY_NAME); + Assert.assertNotNull(dataDecorator); + Assert.assertEquals(Data.class, dataDecorator.getType()); + Assert.assertEquals("0", dataDecorator.toString(d)); + } + + + @Test + public void testReload() throws Exception { + + int nb = provider.getDecorators().size(); + Assert.assertTrue(nb > 0); + + provider.reload(); + + Assert.assertEquals(nb, provider.getDecorators().size()); + } + + @Test + public void testClear() throws Exception { + provider.clear(); + + Assert.assertTrue(provider.getDecorators().isEmpty()); + } + +} diff --git a/src/test/java/org/nuiton/decorator/JXPathContextTester.java b/src/test/java/org/nuiton/decorator/JXPathContextTester.java new file mode 100644 index 0000000..a814cc0 --- /dev/null +++ b/src/test/java/org/nuiton/decorator/JXPathContextTester.java @@ -0,0 +1,56 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.jxpath.JXPathContext; +import org.junit.After; +import org.junit.Assert; + +/** + * A simple class to test JXPath context. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public abstract class JXPathContextTester { + + JXPathContext context; + + @After + public void after() { + context = null; + + } + + protected void newContext(Object o) { + context = JXPathContext.newContext(o); + } + + protected <T> T getValue(String path) { + Assert.assertNotNull("pas de context initialisé", context); + Object value = context.getValue(path); + return (T) value; + } + +} diff --git a/src/test/java/org/nuiton/decorator/JXPathDecoratorTest.java b/src/test/java/org/nuiton/decorator/JXPathDecoratorTest.java new file mode 100644 index 0000000..6b8ca07 --- /dev/null +++ b/src/test/java/org/nuiton/decorator/JXPathDecoratorTest.java @@ -0,0 +1,180 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.junit.After; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class JXPathDecoratorTest { + + protected JXPathDecorator<?> decorator; + + protected String expected; + + protected String result; + + @After + public void after() { + decorator = null; + } + + @Test(expected = NullPointerException.class) + public void testNullInternalClass() throws Exception { + decorator = DecoratorUtil.newJXPathDecorator(null, "hello"); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingRightBrace() throws Exception { + decorator = DecoratorUtil.newJXPathDecorator(Object.class, "${haha"); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingRightBrace2() throws Exception { + decorator = DecoratorUtil.newJXPathDecorator(Object.class, "${haha${hum}"); + } + + @Test + public void testNullBean() throws Exception { + decorator = DecoratorUtil.newJXPathDecorator(Object.class, "hello"); + expected = "hello"; + assertEquals(expected, decorator.getExpression()); + assertEquals(0, decorator.nbToken); + assertEquals(0, decorator.getTokens().length); + + result = decorator.toString(null); + assertEquals(null, result); + } + + @Test + public void testNoJXPath() throws Exception { + decorator = DecoratorUtil.newJXPathDecorator(Object.class, "hello"); + expected = "hello"; + assertEquals(expected, decorator.getExpression()); + assertEquals(0, decorator.nbToken); + assertEquals(0, decorator.getTokens().length); + + result = decorator.toString(this); + assertEquals(expected, result); + } + + @Test + public void testDecorator() throws Exception { + + decorator = DecoratorUtil.newJXPathDecorator( + JXPathDecorator.class, "${expression}$s - ${nbToken}$d"); + assertEquals("%1$s - %2$d", decorator.getExpression()); + assertDecoratorInternal(); + + decorator = DecoratorUtil.newJXPathDecorator( + JXPathDecorator.class, "${expression}${nbToken}"); + assertEquals("%1%2", decorator.getExpression()); + assertDecoratorInternal(); + + decorator = DecoratorUtil.newJXPathDecorator( + JXPathDecorator.class, + "before ${expression}$s - ${nbToken}$d after"); + assertEquals("before %1$s - %2$d after", decorator.getExpression()); + assertDecoratorInternal(); + + decorator = DecoratorUtil.newJXPathDecorator( + JXPathDecorator.class, + "before${expression}$s-${nbToken}$dafter"); + assertEquals("before%1$s-%2$dafter", decorator.getExpression()); + assertDecoratorInternal(); + } + + @Test + public void testDecoratorEspcapeCharacters() throws Exception { + + decorator = DecoratorUtil.newJXPathDecorator( + JXPathDecorator.class, "(${expression}$s) - ${nbToken}$d"); + assertEquals("(%1$s) - %2$d", decorator.getExpression()); + String s = decorator.toString(decorator); + assertDecoratorInternal(); + + } + + @Test + public void testSort() throws Exception { + + List<Data> datas = Data.generate(10); + + JXPathDecorator<Data> d = DecoratorUtil.newJXPathDecorator( + Data.class, "${pos}$d ${name}$s"); + + List<Data> sortData = new ArrayList<Data>(datas); + DecoratorUtil.sort(d, sortData, 0); + for (int i = 0; i < datas.size(); i++) { + Data data = datas.get(i); + Data sData = sortData.get(i); + assertEquals(data, sData); + } + Collections.sort(datas, new Comparator<Data>() { + @Override + public int compare(Data o1, Data o2) { + return o1.name.compareTo(o2.name); + } + }); + JXPathDecorator.Context<Data> context = d.context; + context.setComparator(null); + DecoratorUtil.sort(d, sortData, 1); + for (int i = 0; i < datas.size(); i++) { + Data data = datas.get(i); + Data sData = sortData.get(i); + assertEquals(data, sData); + } + } + + + public void assertDecoratorInternal(String... tokens) { + assertTokens(tokens); + expected = String.format(decorator.getExpression(), + decorator.getExpression(), + decorator.getNbToken()); + result = decorator.toString(decorator); + assertEquals(expected, result); + } + + private void assertTokens(String... tokens) { + if (tokens.length == 0) { + tokens = new String[]{"expression", "nbToken"}; + } + assertEquals(2, decorator.nbToken); + assertEquals(2, decorator.getTokens().length); + assertEquals(tokens[0], decorator.getTokens()[0]); + assertEquals(tokens[1], decorator.getTokens()[1]); + } + +} diff --git a/src/test/java/org/nuiton/decorator/MapPropertyHandlerTest.java b/src/test/java/org/nuiton/decorator/MapPropertyHandlerTest.java new file mode 100644 index 0000000..8592d3e --- /dev/null +++ b/src/test/java/org/nuiton/decorator/MapPropertyHandlerTest.java @@ -0,0 +1,127 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.apache.commons.jxpath.JXPathIntrospector; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Tests the class {@link MapPropertyHandler}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class MapPropertyHandlerTest extends JXPathContextTester { + + + /** Logger */ + private static final Log log = + LogFactory.getLog(MapPropertyHandlerTest.class); + + private static final String TWO_ID = "two"; + + private static final String ONE_ID = "one"; + + @BeforeClass + public static void beforeClass() { + + // pour initialiser le JXPathContext + JXPathIntrospector.registerDynamicClass( + Map.class, + MapPropertyHandler.class + ); + } + + @Before + public void setUp() { + + Map<String, Boolean> map = new TreeMap<String, Boolean>(); + map.put(ONE_ID, true); + map.put(TWO_ID, false); + + if (log.isInfoEnabled()) { + log.info("init map : " + map); + } + newContext(map); + } + + @Test + public void testValues() throws Exception { + + Boolean value; + + value = (Boolean) getValue(".[@name='one']"); + Assert.assertNotNull(value); + Assert.assertTrue(value); + + value = (Boolean) getValue(".[@name='two']"); + Assert.assertNotNull(value); + Assert.assertFalse(value); + + value = (Boolean) getValue(".[@name='three']"); + Assert.assertNull(value); + } + + @Test + public void testKey() throws Exception { + + String key; + + key = (String) getValue(".[@name='key:one']"); + Assert.assertNotNull(key); + Assert.assertEquals(ONE_ID, key); + + key = (String) getValue(".[@name='key:two']"); + Assert.assertNotNull(key); + Assert.assertEquals(TWO_ID, key); + + key = (String) getValue(".[@name='key:fake']"); + Assert.assertNull(key); + } + + @Test + public void testValue() throws Exception { + + Boolean value; + + value = (Boolean) getValue(".[@name='value:true']"); + Assert.assertNotNull(value); + Assert.assertTrue(value); + + value = (Boolean) getValue(".[@name='value:false']"); + Assert.assertNotNull(value); + Assert.assertFalse(value); + + value = (Boolean) getValue(".[@name='value:fake']"); + Assert.assertNull(value); + } +} diff --git a/src/test/java/org/nuiton/decorator/MultiJXPathDecoratorTest.java b/src/test/java/org/nuiton/decorator/MultiJXPathDecoratorTest.java new file mode 100644 index 0000000..3215e13 --- /dev/null +++ b/src/test/java/org/nuiton/decorator/MultiJXPathDecoratorTest.java @@ -0,0 +1,255 @@ +/* + * #%L + * Nuiton Decorator + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.decorator; + +import org.junit.After; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.3 + */ +public class MultiJXPathDecoratorTest { + + protected MultiJXPathDecorator<?> decorator; + + protected String expected; + + protected String result; + + @After + public void after() { + decorator = null; + } + + @Test(expected = NullPointerException.class) + public void testNullInternalClass() throws Exception { + decorator = DecoratorUtil.newMultiJXPathDecorator(null, "hello", "#"); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingRightBrace() throws Exception { + decorator = DecoratorUtil.newMultiJXPathDecorator(Object.class, "${haha", "#"); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingRightBrace2() throws Exception { + decorator = DecoratorUtil.newMultiJXPathDecorator(Object.class, "${haha${hum}", "#"); + } + + @Test + public void testNullBean() throws Exception { + decorator = DecoratorUtil.newMultiJXPathDecorator(Object.class, "hello", ""); + expected = "hello"; + assertEquals(expected, decorator.getExpression()); + assertEquals(0, decorator.nbToken); + assertEquals(0, decorator.getTokens().length); + + result = decorator.toString(null); + assertEquals(null, result); + } + + @Test + public void testMultiDecorator() throws Exception { + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "${expression}$s#${nbToken}$d", "#", " - "); + assertEquals("%1$s - %2$d", decorator.getExpression()); + assertDecoratorInternal(); + assertEquals(2, decorator.contexts.length); + decorator.setContextIndex(1); + assertEquals("%1$d - %2$s", decorator.getExpression()); + assertTokens("nbToken", "expression"); + expected = String.format(decorator.getExpression(), decorator.getNbToken(), decorator.getExpression()); + result = decorator.toString(decorator); + assertEquals(expected, result); + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "${expression}$s ## ${nbToken}$d", " ## ", " - "); + assertEquals("%1$s - %2$d", decorator.getExpression()); + assertDecoratorInternal(); + assertEquals(2, decorator.contexts.length); + decorator.setContextIndex(1); + assertEquals("%1$d - %2$s", decorator.getExpression()); + assertTokens("nbToken", "expression"); + expected = String.format(decorator.getExpression(), decorator.getNbToken(), decorator.getExpression()); + result = decorator.toString(decorator); + assertEquals(expected, result); + } + + @Test + public void testMultiDecorator2() throws Exception { + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "${expression}$s#${nbToken}$d#${separator}$s", "#", " - "); + + assertEquals("%1$s - %2$d - %3$s", decorator.getExpression()); + assertTokens("expression", "nbToken", "separator"); + assertEquals(3, decorator.contexts.length); + + expected = String.format(decorator.getExpression(), decorator.getExpression(), decorator.getNbToken(), decorator.getSeparator()); + result = decorator.toString(decorator); + assertEquals(expected, result); + + decorator.setContextIndex(1); + assertEquals("%1$d - %2$s - %3$s", decorator.getExpression()); + assertTokens("nbToken", "separator", "expression"); + expected = String.format(decorator.getExpression(), decorator.getNbToken(), decorator.getSeparator(), decorator.getExpression()); + result = decorator.toString(decorator); + assertEquals(expected, result); + + decorator.setContextIndex(2); + assertEquals("%1$s - %2$s - %3$d", decorator.getExpression()); + assertTokens("separator", "expression", "nbToken"); + + expected = String.format(decorator.getExpression(), decorator.getSeparator(), decorator.getExpression(), decorator.getNbToken()); + result = decorator.toString(decorator); + assertEquals(expected, result); + } + + + @Test + public void testDecoratorEspcapeCharacters() throws Exception { + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "(${expression}$s)#${nbToken}$d", "#", " - "); + assertEquals("(%1$s) - %2$d", decorator.getExpression()); + assertTokens("expression", "nbToken"); + assertEquals(2, decorator.contexts.length); + expected = String.format(decorator.getExpression(), decorator.getExpression(), decorator.getNbToken()); + result = decorator.toString(decorator); + System.out.println("s=" + result); + assertEquals(expected, result); + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "${nbToken}$d#(${expression}$s)", "#", " - "); + assertEquals("%1$d - (%2$s)", decorator.getExpression()); + assertTokens("nbToken", "expression"); + assertEquals(2, decorator.contexts.length); + expected = String.format(decorator.getExpression(), decorator.getNbToken(), decorator.getExpression()); + result = decorator.toString(decorator); + System.out.println("s=" + result); + assertEquals(expected, result); + + DecoratorProvider provider = new MyDummyDecoratorProvider(); + provider.registerMultiJXPathDecorator(MultiJXPathDecorator.class, "(${expression}$s)#${nbToken}$d", "#", " - "); + decorator = (MultiJXPathDecorator<?>) provider.getDecoratorByType(MultiJXPathDecorator.class); + + assertEquals("(%1$s) - %2$d", decorator.getExpression()); + assertTokens("expression", "nbToken"); + assertEquals(2, decorator.contexts.length); + expected = String.format(decorator.getExpression(), decorator.getExpression(), decorator.getNbToken()); + result = decorator.toString(decorator); + System.out.println("s=" + result); + assertEquals(expected, result); + + } + + @Test + public void testMultiDecoratorWithMultiRef() throws Exception { + + decorator = DecoratorUtil.newMultiJXPathDecorator(JXPathDecorator.class, "${expression}$s#${nbToken}$d#${separator}$s %3$s", "#", " - "); + + assertEquals("%1$s - %2$d - %3$s %3$s", decorator.getExpression()); + assertTokens("expression", "nbToken", "separator"); + assertEquals(3, decorator.contexts.length); + + expected = String.format(decorator.getExpression(), decorator.getExpression(), decorator.getNbToken(), decorator.getSeparator()); + result = decorator.toString(decorator); + assertEquals(expected, result); + + decorator.setContextIndex(1); + assertEquals("%1$d - %2$s %2$s - %3$s", decorator.getExpression()); + assertTokens("nbToken", "separator", "expression"); + expected = String.format(decorator.getExpression(), decorator.getNbToken(), decorator.getSeparator(), decorator.getExpression()); + result = decorator.toString(decorator); + assertEquals(expected, result); + + decorator.setContextIndex(2); + assertEquals("%1$s %1$s - %2$s - %3$d", decorator.getExpression()); + assertTokens("separator", "expression", "nbToken"); + + expected = String.format(decorator.getExpression(), decorator.getSeparator(), decorator.getExpression(), decorator.getNbToken()); + result = decorator.toString(decorator); + assertEquals(expected, result); + } + + @Test + public void testSort() throws Exception { + + List<Data> datas = Data.generate(10); + + MultiJXPathDecorator<Data> d = DecoratorUtil.newMultiJXPathDecorator(Data.class, "${pos}$d-${name}$s", "-"); + + List<Data> sortData = new ArrayList<Data>(datas); + DecoratorUtil.sort(d, sortData, 0); + for (int i = 0; i < datas.size(); i++) { + Data data = datas.get(i); + Data sData = sortData.get(i); + assertEquals(data, sData); + } + Collections.sort(datas, new Comparator<Data>() { + + @Override + public int compare(Data o1, Data o2) { + return o1.name.compareTo(o2.name); + } + }); + d.setContextIndex(1); + DecoratorUtil.sort(d, sortData, 1); + for (int i = 0; i < datas.size(); i++) { + Data data = datas.get(i); + Data sData = sortData.get(i); + assertEquals(data, sData); + } + } + + public void assertDecoratorInternal(String... tokens) { + assertTokens(tokens); + expected = String.format(decorator.getExpression(), decorator.getExpression(), decorator.getNbToken()); + result = decorator.toString(decorator); + assertEquals(expected, result); + } + + private void assertTokens(String... tokens) { + if (tokens.length == 0) { + tokens = new String[]{"expression", "nbToken"}; + } + assertEquals(tokens.length, decorator.nbToken); + assertEquals(tokens.length, decorator.getTokens().length); + for (int i = 0; i < tokens.length; i++) { + assertEquals(tokens[i], decorator.getTokens()[i]); + } + } + + private static class MyDummyDecoratorProvider extends DecoratorProvider { + + @Override + protected void loadDecorators() { + } + + } +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties new file mode 100644 index 0000000..dd8f192 --- /dev/null +++ b/src/test/resources/log4j.properties @@ -0,0 +1,34 @@ +### +# #%L +# Nuiton Utils +# +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Global logging configuration +log4j.rootLogger=ERROR, stdout + +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n + +# package level +log4j.logger.org.nuiton.decorator=INFO -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.