svn-gvsig-desktop / tags / v1_0_2_RELEASE / applications / appgvSIG / src / com / vividsolutions / jump / util / FlexibleDateParser.java @ 11432
History | View | Annotate | Download (10.4 KB)
1 |
/*
|
---|---|
2 |
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
|
3 |
* for visualizing and manipulating spatial features with geometry and attributes.
|
4 |
*
|
5 |
* Copyright (C) 2003 Vivid Solutions
|
6 |
*
|
7 |
* This program is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU General Public License
|
9 |
* as published by the Free Software Foundation; either version 2
|
10 |
* of the License, or (at your option) any later version.
|
11 |
*
|
12 |
* This program is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
* GNU General Public License for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU General Public License
|
18 |
* along with this program; if not, write to the Free Software
|
19 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
20 |
*
|
21 |
* For more information, contact:
|
22 |
*
|
23 |
* Vivid Solutions
|
24 |
* Suite #1A
|
25 |
* 2328 Government Street
|
26 |
* Victoria BC V8T 5G5
|
27 |
* Canada
|
28 |
*
|
29 |
* (250)385-6040
|
30 |
* www.vividsolutions.com
|
31 |
*/
|
32 |
package com.vividsolutions.jump.util; |
33 |
|
34 |
import java.awt.Color; |
35 |
import java.awt.Component; |
36 |
import java.io.IOException; |
37 |
import java.io.InputStream; |
38 |
import java.text.DateFormat; |
39 |
import java.text.ParseException; |
40 |
import java.text.ParsePosition; |
41 |
import java.text.SimpleDateFormat; |
42 |
import java.util.ArrayList; |
43 |
import java.util.Calendar; |
44 |
import java.util.Collection; |
45 |
import java.util.Comparator; |
46 |
import java.util.Date; |
47 |
import java.util.Iterator; |
48 |
import java.util.TreeSet; |
49 |
|
50 |
import javax.swing.DefaultCellEditor; |
51 |
import javax.swing.JComponent; |
52 |
import javax.swing.JTable; |
53 |
import javax.swing.JTextField; |
54 |
import javax.swing.border.LineBorder; |
55 |
|
56 |
import com.vividsolutions.jts.util.Assert; |
57 |
|
58 |
/**
|
59 |
* Warning: This class can parse a wide variety of formats. This flexibility is fine for parsing user
|
60 |
* input because the user immediately sees whether the parser is correct and can fix it if
|
61 |
* necessary. However, GML files are advised to stick with a safe format like yyyy-MM-dd.
|
62 |
* yy/MM/dd is not as safe because while 99/03/04 will be parsed as yyyy/MM/dd,
|
63 |
* 02/03/04 will be parsed as MM/dd/yyyy (because MM/dd/yyyy appears earlier than yyyy/MM/dd
|
64 |
* in FlexibleDateParser.txt).
|
65 |
*/
|
66 |
public class FlexibleDateParser { |
67 |
private static Collection lenientFormatters = null; |
68 |
private static Collection unlenientFormatters = null; |
69 |
//CellEditor used to be a static field CELL_EDITOR, but I was getting
|
70 |
//problems calling it from ESETextField (it simply didn't appear).
|
71 |
//The problems vanished when I turned it into a static class. I didn't
|
72 |
//investigate further. [Jon Aquino]
|
73 |
public static final class CellEditor extends DefaultCellEditor { |
74 |
public CellEditor() { |
75 |
super(new JTextField()); |
76 |
} |
77 |
private Object value; |
78 |
private FlexibleDateParser parser = new FlexibleDateParser(); |
79 |
|
80 |
public boolean stopCellEditing() { |
81 |
try {
|
82 |
value = parser.parse((String) super.getCellEditorValue(), true); |
83 |
} catch (Exception e) { |
84 |
((JComponent) getComponent()).setBorder(new LineBorder(Color.red)); |
85 |
|
86 |
return false; |
87 |
} |
88 |
|
89 |
return super.stopCellEditing(); |
90 |
} |
91 |
|
92 |
public Component getTableCellEditorComponent( |
93 |
JTable table,
|
94 |
Object value,
|
95 |
boolean isSelected,
|
96 |
int row,
|
97 |
int column) {
|
98 |
this.value = null; |
99 |
((JComponent) getComponent()).setBorder(new LineBorder(Color.black)); |
100 |
|
101 |
return super.getTableCellEditorComponent( |
102 |
table, |
103 |
format((Date) value),
|
104 |
isSelected, |
105 |
row, |
106 |
column); |
107 |
} |
108 |
|
109 |
private String format(Date date) { |
110 |
return (date == null) ? "" : formatter.format(date); |
111 |
} |
112 |
|
113 |
public Object getCellEditorValue() { |
114 |
return value;
|
115 |
} |
116 |
|
117 |
//Same formatter as used by JTable.DateRenderer. [Jon Aquino]
|
118 |
private DateFormat formatter = DateFormat.getDateInstance(); |
119 |
}; |
120 |
|
121 |
private boolean verbose = false; |
122 |
|
123 |
private Collection sortByComplexity(Collection patterns) { |
124 |
//Least complex to most complex. [Jon Aquino]
|
125 |
TreeSet sortedPatterns = new TreeSet(new Comparator() { |
126 |
public int compare(Object o1, Object o2) { |
127 |
int result = complexity(o1.toString()) - complexity(o2.toString());
|
128 |
if (result == 0) { |
129 |
//The two patterns have the same level of complexity.
|
130 |
//Sort by order of appearance (e.g. to resolve
|
131 |
//MM/dd/yyyy vs dd/MM/yyyy [Jon Aquino]
|
132 |
result = ((Pattern) o1).index - ((Pattern) o2).index; |
133 |
} |
134 |
return result;
|
135 |
} |
136 |
|
137 |
private TreeSet uniqueCharacters = new TreeSet(); |
138 |
|
139 |
private int complexity(String pattern) { |
140 |
uniqueCharacters.clear(); |
141 |
|
142 |
for (int i = 0; i < pattern.length(); i++) { |
143 |
if (("" + pattern.charAt(i)).trim().length() > 0) { |
144 |
uniqueCharacters.add("" + pattern.charAt(i));
|
145 |
} |
146 |
} |
147 |
|
148 |
return uniqueCharacters.size();
|
149 |
} |
150 |
}); |
151 |
sortedPatterns.addAll(patterns); |
152 |
|
153 |
return sortedPatterns;
|
154 |
} |
155 |
|
156 |
private Collection lenientFormatters() { |
157 |
if (lenientFormatters == null) { |
158 |
load(); |
159 |
} |
160 |
|
161 |
return lenientFormatters;
|
162 |
} |
163 |
|
164 |
private Collection unlenientFormatters() { |
165 |
if (unlenientFormatters == null) { |
166 |
load(); |
167 |
} |
168 |
|
169 |
return unlenientFormatters;
|
170 |
} |
171 |
|
172 |
/**
|
173 |
* @return null if s is empty
|
174 |
*/
|
175 |
public Date parse(String s, boolean lenient) throws ParseException { |
176 |
if (s.trim().length() == 0) { |
177 |
return null; |
178 |
} |
179 |
//The deprecated Date#parse method is actually pretty flexible. [Jon Aquino]
|
180 |
try {
|
181 |
if (verbose) {
|
182 |
System.out.println(s + " -- Date constructor"); |
183 |
} |
184 |
return new Date(s); |
185 |
} catch (Exception e) { |
186 |
//Eat it. [Jon Aquino]
|
187 |
} |
188 |
|
189 |
try {
|
190 |
return parse(s, unlenientFormatters());
|
191 |
} catch (ParseException e) { |
192 |
if (lenient) {
|
193 |
return parse(s, lenientFormatters());
|
194 |
} |
195 |
|
196 |
throw e;
|
197 |
} |
198 |
} |
199 |
|
200 |
private Date parse(String s, Collection formatters) throws ParseException { |
201 |
ParseException firstParseException = null; |
202 |
|
203 |
for (Iterator i = formatters.iterator(); i.hasNext();) { |
204 |
SimpleDateFormat formatter = (SimpleDateFormat) i.next(); |
205 |
|
206 |
if (verbose) {
|
207 |
System.out.println(
|
208 |
s |
209 |
+ " -- "
|
210 |
+ formatter.toPattern() |
211 |
+ (formatter.isLenient() ? "lenient" : "")); |
212 |
} |
213 |
|
214 |
try {
|
215 |
return parse(s, formatter);
|
216 |
} catch (ParseException e) { |
217 |
if (firstParseException == null) { |
218 |
firstParseException = e; |
219 |
} |
220 |
} |
221 |
} |
222 |
|
223 |
throw firstParseException;
|
224 |
} |
225 |
|
226 |
private Date parse(String s, SimpleDateFormat formatter) throws ParseException { |
227 |
ParsePosition pos = new ParsePosition(0); |
228 |
Date date = formatter.parse(s, pos);
|
229 |
|
230 |
if (pos.getIndex() == 0) { |
231 |
throw new ParseException( |
232 |
"Unparseable date: \"" + s + "\"", |
233 |
pos.getErrorIndex()); |
234 |
} |
235 |
|
236 |
//SimpleDateFormat ignores trailing characters in the pattern string that it
|
237 |
//doesn't need. Don't allow it to ignore any characters. [Jon Aquino]
|
238 |
if (pos.getIndex() != s.length()) {
|
239 |
throw new ParseException( |
240 |
"Unparseable date: \"" + s + "\"", |
241 |
pos.getErrorIndex()); |
242 |
} |
243 |
|
244 |
Calendar calendar = Calendar.getInstance(); |
245 |
calendar.setTime(date); |
246 |
|
247 |
if ((calendar.get(Calendar.YEAR) == 1970) && (s.indexOf("70") == -1)) { |
248 |
calendar.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR)); |
249 |
} |
250 |
|
251 |
return calendar.getTime();
|
252 |
} |
253 |
|
254 |
private static class Pattern { |
255 |
private String pattern; |
256 |
private int index; |
257 |
public Pattern(String pattern, int index) { |
258 |
this.pattern = pattern;
|
259 |
this.index = index;
|
260 |
} |
261 |
public String toString() { |
262 |
return pattern;
|
263 |
} |
264 |
} |
265 |
|
266 |
private void load() { |
267 |
if (lenientFormatters == null) { |
268 |
InputStream inputStream =
|
269 |
getClass().getResourceAsStream("FlexibleDateParser.txt");
|
270 |
|
271 |
try {
|
272 |
try {
|
273 |
Collection patterns = new ArrayList(); |
274 |
int index = 0; |
275 |
for (Iterator i = FileUtil.getContents(inputStream).iterator(); |
276 |
i.hasNext(); |
277 |
) { |
278 |
String line = ((String) i.next()).trim(); |
279 |
|
280 |
if (line.startsWith("#")) { |
281 |
continue;
|
282 |
} |
283 |
|
284 |
if (line.length() == 0) { |
285 |
continue;
|
286 |
} |
287 |
|
288 |
patterns.add(new Pattern(line, index)); |
289 |
index++; |
290 |
} |
291 |
|
292 |
unlenientFormatters = toFormatters(false, patterns);
|
293 |
lenientFormatters = toFormatters(true, patterns);
|
294 |
} finally {
|
295 |
inputStream.close(); |
296 |
} |
297 |
} catch (IOException e) { |
298 |
Assert.shouldNeverReachHere(e.toString()); |
299 |
} |
300 |
} |
301 |
} |
302 |
|
303 |
private Collection toFormatters(boolean lenient, Collection patterns) { |
304 |
ArrayList formatters = new ArrayList(); |
305 |
//Sort from least complex to most complex; otherwise, ddMMMyyyy
|
306 |
//instead of MMMd will match "May 15". [Jon Aquino]
|
307 |
for (Iterator i = sortByComplexity(patterns).iterator(); i.hasNext();) { |
308 |
Pattern pattern = (Pattern) i.next(); |
309 |
SimpleDateFormat formatter = new SimpleDateFormat(pattern.pattern); |
310 |
formatter.setLenient(lenient); |
311 |
formatters.add(formatter); |
312 |
} |
313 |
|
314 |
return formatters;
|
315 |
} |
316 |
|
317 |
public static void main(String[] args) throws Exception { |
318 |
System.out.println(DateFormat.getDateInstance().parse("03-Mar-1998")); |
319 |
} |
320 |
|
321 |
public void setVerbose(boolean b) { |
322 |
verbose = b; |
323 |
} |
324 |
} |