View Javadoc
1   /*
2    * Copyright 2012 Olivier Godineau
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at http://www.apache.org/licenses/LICENSE-2.0
7    * 
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11   * License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package olg.csv.bean.loader;
15  
16  import java.util.Date;
17  import java.util.Locale;
18  
19  import javax.xml.xpath.XPathConstants;
20  import javax.xml.xpath.XPathExpressionException;
21  
22  import olg.csv.bean.formatter.Formatter;
23  
24  import org.w3c.dom.Element;
25  import org.w3c.dom.Node;
26  
27  /**
28   * 
29   * Class dedicated to load a formatter {@link Formatter} from an XML element
30   * conformed to XML schema FormatterType specification. Each concret class shall
31   * have the responsibility to load a concrete Formatter class.
32   * 
33   * <p>
34   * Class based on Chain of responsability pattern and used to chain concret
35   * Formatter Loaders.
36   * </p>
37   * 
38   * @author Olivier Godineau
39   * 
40   */
41  public abstract class AbstractFormatterLoader {
42  	/**
43  	 * Singleton instance to use to load Formatter.
44  	 */
45  	private static AbstractFormatterLoader instance = new DateFormatterLoader(new CustomFormatterLoader(null));
46  
47  	/**
48  	 * Returns singleton responsible of formatters loading.
49  	 * 
50  	 * This is this instance you have to be used to load every type of
51  	 * formatters from XML element conformed to XML Schema FormatterType
52  	 * specification.
53  	 * 
54  	 * @return the AbstractFormatter loader
55  	 */
56  	public static AbstractFormatterLoader getInstance() {
57  		return instance;
58  	}
59  
60  	/**
61  	 * FormatterLoader successor as describe in Chain of responsability pattern.
62  	 */
63  	protected AbstractFormatterLoader successor;
64  
65  	/**
66  	 * This Xpath expression allows to identify xml node which describe the
67  	 * corresponding concret formatter under formatterType node.
68  	 */
69  	protected String xPathExpression;
70  
71  	/**
72  	 * Returns a concret formatter identified from the given XML element.
73  	 * 
74  	 * @param element
75  	 *            XML node corresponding to correspondant to XML FormatterType
76  	 *            as described in our XML schema.
77  	 * @return a concret formatter or <code>null</code> if the given element
78  	 *         doesn't match the concret formatter type this loader has
79  	 *         responsability and doesn't match any successor
80  	 * @throws XPathExpressionException
81  	 *             on invalid XPathExpression
82  	 * @throws LoadException
83  	 *             on Error occurs during loading
84  	 */
85  	public final Formatter<?> getFormatter(Element element) throws XPathExpressionException, LoadException {
86  		Formatter<?> retour = null;
87  
88  		if (element != null) {
89  			Node node = (Node) Util.evaluerDOM(element, xPathExpression, XPathConstants.NODE);
90  			if (node != null) { // NOPMD by olivier on 01/02/12 00:43
91  				retour = getConcreteFormatter((Element) node);
92  			} else {
93  				if (successor != null) {
94  					retour = successor.getFormatter(element);
95  				}
96  			}
97  		}
98  		return retour;
99  	}
100 
101 	/**
102 	 * 
103 	 * Returns a concret formatter.
104 	 * 
105 	 * @param node
106 	 *            XML Element corresponding to the XPath expression. From this
107 	 *            element a concret formatter this loader should return will be
108 	 *            extracted
109 	 * 
110 	 * @return a concret formatter this loader has responsability or
111 	 *         <code>null</code> if the given node doesn't match the type this
112 	 *         loader should return
113 	 * @throws XPathExpressionException
114 	 *             on invalid XPathExpression
115 	 * @throws LoadException
116 	 *             on Error occurs during loading
117 	 * @see #xPathExpression
118 	 */
119 	protected abstract Formatter<?> getConcreteFormatter(Element node) throws XPathExpressionException, LoadException;
120 
121 	/**
122 	 * 
123 	 * @param xPathExpression
124 	 *            XPath expression that allows to identify the concret formatter
125 	 *            under the XML FormatterType node
126 	 * @param successor
127 	 *            following concret loader in the chain of responsability
128 	 * 
129 	 */
130 	protected AbstractFormatterLoader(String xPathExpression, AbstractFormatterLoader successor) {
131 		super();
132 		this.successor = successor;
133 		this.xPathExpression = xPathExpression;
134 	}
135 
136 	/**
137 	 * Class responsible of DateFormatter loading.
138 	 * 
139 	 * @see Formatter#getDateFormatter(String, Locale)
140 	 */
141 	private static class DateFormatterLoader extends AbstractFormatterLoader {
142 
143 		/**
144 		 * 
145 		 * @param successor
146 		 *            the next loader in the formatter loaders chain.
147 		 */
148 		protected DateFormatterLoader(AbstractFormatterLoader successor) {
149 			super("date", successor);
150 
151 		}
152 
153 		@Override
154 		protected Formatter<? extends Date> getConcreteFormatter(Element node) throws XPathExpressionException,
155 				LoadException {
156 			String loc = node.getAttribute("locale");
157 			Locale locale = Util.getLocale(loc);
158 			if (!"".equals(loc) && locale == null) {
159 				throw new LoadException("Locale based on expression[" + loc + "] is not available ");
160 			}
161 
162 			return Formatter.getDateFormatter(node.getAttribute("format"), locale);
163 		}
164 
165 	}
166 
167 	/**
168 	 * Class responsible of CustomFormatter loading.
169 	 * 
170 	 * @see CustomLoader
171 	 */
172 	private static class CustomFormatterLoader extends AbstractFormatterLoader {
173 
174 		/**
175 		 * 
176 		 * @param successor
177 		 *            the next loader in the formatter loaders chain.
178 		 */
179 		protected CustomFormatterLoader(AbstractFormatterLoader successor) {
180 			super("custom", successor);
181 
182 		}
183 
184 		@Override
185 		protected Formatter<?> getConcreteFormatter(Element node) throws XPathExpressionException, LoadException {
186 			Object object = CustomLoader.getBean(node);
187 			if (!(object instanceof Formatter)) {
188 				throw new LoadException(object.getClass() + " must extend " + Formatter.class);
189 			}
190 
191 			return (Formatter<?>) object;
192 		}
193 	}
194 }