001: package com.salmonllc.html;
002:
003: /////////////////////////
004: //$Archive: /SOFIA/SourceCode/com/salmonllc/html/HtmlStateComponent.java $
005: //$Author: Dan $
006: //$Revision: 17 $
007: //$Modtime: 3/19/04 11:43a $
008: /////////////////////////
009:
010: import java.util.Arrays;
011: import java.util.Comparator;
012: import java.util.Hashtable;
013:
014: import com.salmonllc.html.events.ValueChangedEvent;
015:
016: /**
017: * Implements a drop down list that allows the user to select US states, Canadian Provinces or countries.
018: * The name "state" is historical; it oringally only supported US states.
019: */
020: public class HtmlStateComponent extends HtmlFormComponent {
021: // This will become a J2SE 1.5 Enum sometime around the year 2010 due to backwards compatibility.
022: protected static final int LIST_STATES = 0, LIST_PROVINCES = 1,
023: LIST_STATEPROV = 2, LIST_PROVSTATE = 3, LIST_CANUSA = 4,
024: LIST_COUNTRIES = 5;
025: protected int listType = LIST_STATES;
026: // Must stay in synch with the above int values.
027: protected final static String[] listTypes = { "states",
028: "provinces", "statesprovinces", "provincesstates",
029: "canusa", "countries" };
030: public static final String DEFAULT_LIST_TYPE = listTypes[0];
031: private Integer _tabIndex;
032: protected HtmlDropDownList _state;
033:
034: /** The US States */
035: protected static String[][] STATES = { { "AL", "Alabama" },
036: { "AK", "Alaska" }, { "AZ", "Arizona" },
037: { "AR", "Arkansas" }, { "CA", "California" },
038: { "CO", "Colorado" }, { "CT", "Connecticut" },
039: { "DE", "Delaware" }, { "DC", "District of Columbia" },
040: { "FL", "Florida" }, { "GA", "Georgia" },
041: { "HI", "Hawaii" }, { "ID", "Idaho" },
042: { "IL", "Illinois" }, { "IN", "Indiana" },
043: { "IA", "Iowa" }, { "KS", "Kansas" }, { "KY", "Kentucky" },
044: { "LA", "Louisiana" }, { "ME", "Maine" },
045: { "MD", "Maryland" }, { "MA", "Massachusetts" },
046: { "MI", "Michigan" }, { "MN", "Minnesota" },
047: { "MS", "Mississippi" }, { "MO", "Missouri" },
048: { "MT", "Montana" }, { "NE", "Nebraska" },
049: { "NV", "Nevada" }, { "NH", "New Hampshire" },
050: { "NJ", "New Jersey" }, { "NM", "New Mexico" },
051: { "NY", "New York" }, { "NC", "North Carolina" },
052: { "ND", "North Dakota" }, { "OH", "Ohio" },
053: { "OK", "Oklahoma" }, { "OR", "Oregon" },
054: { "PA", "Pennsylvania" }, { "RI", "Rhode Island" },
055: { "SC", "South Carolina" }, { "SD", "South Dakota" },
056: { "TN", "Tennessee" }, { "TX", "Texas" }, { "UT", "Utah" },
057: { "VT", "Vermont" }, { "VA", "Virginia" },
058: { "WA", "Washington" }, { "WV", "West Virginia" },
059: { "WI", "Wisconsin" }, { "WY", "Wyoming" } };
060: // Optimization: geneate this statically as it is likely the most common one overall.
061: protected final static String[] STATECODES = getSortedCodes(STATES);
062:
063: /**The Canadian Provinces.
064: * REFERENCE: gov't-run post office site http://www.canadapost.ca/tools/pcl/bin/default-e.asp,
065: */
066: protected static String[][] PROVINCES = { { "AB", "Alberta" },
067: { "BC", "British Columbia" }, { "MB", "Manitoba" },
068: { "NB", "New Brunswick" },
069: { "NL", "Newfoundland and Labrador" },
070: { "NS", "Nova Scotia" }, { "NT", "Northwest Territories" },
071: { "NU", "Nunavut" }, { "ON", "Ontario" },
072: { "PE", "Prince Edward Island" }, { "QC", "Quebec" },
073: { "SK", "Saskatchewan" }, { "YK", "Yukon" }, };
074:
075: /**The countries, with their 2-letter ISO country codes, which are lower case */
076: protected static String[][] COUNTRIES = {
077: // These first two are duplicates for the obvious optimization that
078: // most SOFIA users will be in North America initially. -- ian
079: { "us", "U.S.A." }, { "ca", "Canada" },
080: { "al", "Albania" }, { "dz", "Algeria" },
081: { "as", "American Samoa" }, { "ad", "Andorra" },
082: { "ao", "Angola" }, { "ai", "Anguilla" },
083: { "ag", "Antigua" }, { "ar", "Argentina" },
084: { "am", "Armenia" }, { "aw", "Aruba" },
085: { "au", "Australia" }, { "at", "Austria" },
086: { "az", "Azerbaijan" }, { "bs", "Bahamas" },
087: { "bh", "Bahrain" }, { "bd", "Bangladesh" },
088: { "bb", "Barbados" }, { "ag", "Barbuda" },
089: { "by", "Belarus" }, { "be", "Belgium" },
090: { "bz", "Belize" }, { "bj", "Benin" }, { "bm", "Bermuda" },
091: { "bt", "Bhutan" }, { "bo", "Bolivia" },
092: { "an", "Bonaire" }, { "bw", "Botswana" },
093: { "br", "Brazil" }, { "vg", "British Virgin Islands" },
094: { "bn", "Brunei" }, { "bg", "Bulgaria" },
095: { "bf", "Burkina Faso" }, { "bi", "Burundi" },
096: { "kh", "Cambodia" }, { "cm", "Cameroon" },
097: { "ca", "Canada" }, { "cv", "Cape Verde" },
098: { "ky", "Cayman Islands" },
099: { "cf", "Central African Republic" }, { "td", "Chad" },
100: { "cs", "Channel Islands" }, { "cl", "Chile" },
101: { "cn", "China" }, { "co", "Colombia" },
102: { "cg", "Congo-Brazzaville" }, { "zr", "Congo-Kinshasa" },
103: { "ck", "Cook Islands" }, { "cr", "Costa Rica" },
104: { "hr", "Croatia" }, { "cu", "Cuba" }, { "an", "Curacao" },
105: { "cy", "Cyprus" }, { "cz", "Czech Republic" },
106: { "dk", "Denmark" }, { "dj", "Djibouti" },
107: { "dm", "Dominica" }, { "do", "Dominican Republic" },
108: { "ec", "Ecuador" }, { "eg", "Egypt" },
109: { "sv", "El Salvador" }, { "gq", "Equatorial Guinea" },
110: { "er", "Eritrea" }, { "ee", "Estonia" },
111: { "et", "Ethiopia" }, { "fo", "Faroe Islands" },
112: { "fj", "Fiji" }, { "fi", "Finland" }, { "fr", "France" },
113: { "gf", "French Guiana" }, { "pf", "French Polynesia" },
114: { "ga", "Gabon" }, { "gm", "Gambia" }, { "ge", "Georgia" },
115: { "de", "Germany" }, { "gh", "Ghana" },
116: { "gi", "Gibraltar" }, { "gr", "Greece" },
117: { "gl", "Greenland" }, { "gd", "Grenada" },
118: { "gp", "Guadeloupe" }, { "gu", "Guam" },
119: { "gt", "Guatemala" }, { "gw", "Guinea Bissau" },
120: { "gn", "Guinea" }, { "gy", "Guyana" }, { "ht", "Haiti" },
121: { "hn", "Honduras" }, { "hk", "Hong Kong" },
122: { "hu", "Hungary" }, { "is", "Iceland" },
123: { "in", "India" }, { "id", "Indonesia" }, { "ir", "Iran" },
124: { "iq", "Iraq" }, { "ie", "Ireland" }, { "il", "Israel" },
125: { "it", "Italy" }, { "ci", "Ivory Coast" },
126: { "jm", "Jamaica" }, { "jp", "Japan" }, { "jo", "Jordan" },
127: { "kz", "Kazakhstan" }, { "ke", "Kenya" },
128: { "kw", "Kuwait" }, { "kg", "Kyrgyzstan" },
129: { "la", "Laos" }, { "lv", "Latvia" }, { "lb", "Lebanon" },
130: { "ls", "Lesotho" }, { "lr", "Liberia" },
131: { "ly", "Libya" }, { "li", "Liechtenstein" },
132: { "lt", "Lithuania" }, { "lu", "Luxembourg" },
133: { "mo", "Macau" }, { "mk", "Macedonia" },
134: { "mg", "Madagascar" }, { "mw", "Malawi" },
135: { "my", "Malaysia" }, { "ml", "Mali" }, { "mt", "Malta" },
136: { "mh", "Marshall Islands" }, { "mq", "Martinique" },
137: { "mr", "Mauritania" }, { "mu", "Mauritius" },
138: { "mx", "Mexico" }, { "fm", "Micronesia" },
139: { "md", "Moldova" }, { "mc", "Monaco" },
140: { "mn", "Mongolia" }, { "ms", "Montserrat" },
141: { "ma", "Morocco" }, { "mz", "Mozambique" },
142: { "mm", "MyanmarBurma" }, { "na", "Namibia" },
143: { "np", "Nepal" }, { "an", "Netherlands Antilles" },
144: { "nl", "Netherlands" }, { "nc", "New Caledonia" },
145: { "nz", "New Zealand" }, { "ni", "Nicaragua" },
146: { "ne", "Niger" }, { "ng", "Nigeria" }, { "no", "Norway" },
147: { "om", "Oman" }, { "pk", "Pakistan" }, { "pw", "Palau" },
148: { "pa", "Panama" }, { "pg", "Papua New Guinea" },
149: { "py", "Paraguay" }, { "pe", "Peru" },
150: { "ph", "Philippines" }, { "pl", "Poland" },
151: { "pt", "Portugal" }, { "pr", "Puerto Rico" },
152: { "qa", "Qatar" }, { "re", "Reunion" },
153: { "ro", "Romania" }, { "ru", "Russia" },
154: { "rw", "Rwanda" }, { "an", "Saba" }, { "mp", "Saipan" },
155: { "sm", "San Marino" }, { "sa", "Saudi Arabia" },
156: { "sn", "Senegal" }, { "sc", "Seychelles" },
157: { "sl", "Sierra Leone" }, { "sg", "Singapore" },
158: { "sk", "Slovak Republic" }, { "si", "Slovenia" },
159: { "so", "Somalia" }, { "za", "South Africa" },
160: { "kr", "South Korea" }, { "es", "Spain" },
161: { "lk", "Sri Lanka" }, { "gp_EN", "St. Barthelemy" },
162: { "vi", "St. Croix" }, { "an", "St. Eustatius" },
163: { "kn", "St. Kitts and Nevis" }, { "lc", "St. Lucia" },
164: { "an", "St. Maarten/St. Martin" }, { "vi", "St. Thomas" },
165: { "vc", "St. Vincent" }, { "sd", "Sudan" },
166: { "sr", "Suriname" }, { "sz", "Swaziland" },
167: { "se", "Sweden" }, { "ch", "Switzerland" },
168: { "sy", "Syria" }, { "tw", "Taiwan" },
169: { "tz", "Tanzania" }, { "th", "Thailand" },
170: { "tg", "Togo" }, { "vg", "Tortola" },
171: { "tt", "Trinidad and Tobago" }, { "tn", "Tunisia" },
172: { "tr", "Turkey" }, { "tm", "Turkmenistan" },
173: { "tc", "Turks and Caicos Islands" },
174: { "vi", "U.S. Virgin Islands" }, { "ug", "Uganda" },
175: { "ua", "Ukraine" }, { "ae", "United Arab Emirates" },
176: { "gb", "United Kingdom" }, { "uy", "Uruguay" },
177: { "us", "U.S.A." }, { "uz", "Uzbekistan" },
178: { "vu", "Vanuatu" }, { "va", "Vatican City" },
179: { "ve", "Venezuela" }, { "vn", "Vietnam" },
180: { "wf", "Wallis & Futuna" }, { "ye", "Yemen" },
181: { "zm", "Zambia" }, { "zw", "Zimbabwe" }, };
182:
183: /**
184: * StateComponent constructor, using default list type.
185: *
186: * @param name Name of component.
187: * @param p Page containing component.
188: * @param codesonly indicates whether to display codesonly on full state names.
189: */
190: public HtmlStateComponent(String name, HtmlPage page,
191: boolean codesOnly) {
192: this (name, page, DEFAULT_LIST_TYPE, codesOnly);
193: }
194:
195: /**
196: * StateComponent constructor specifying list type.
197: *
198: * @param name Name of component.
199: * @param p Page containing component.
200: * @param codesonly indicates whether to display codesonly on full state names.
201: */
202: public HtmlStateComponent(String name, HtmlPage p,
203: String listTypeName, boolean codesonly) {
204: super (name, p);
205: _state = new HtmlDropDownList(name, p);
206: _state.addOption(" ", " ");
207: if (listTypeName == null) {
208: // Nothing to do, take default.
209: } else {
210: for (int i = 0; i < listTypes.length; i++) {
211: if (listTypeName.equalsIgnoreCase(listTypes[i])) {
212: listType = i;
213: break;
214: }
215: }
216: }
217: String[][] data = STATES;
218: String[] codes = STATECODES;
219: switch (listType) {
220: case LIST_STATES:
221: data = STATES;
222: codes = STATECODES;
223: break;
224: // Use Lazy Evaluation of codes lists for the remaining listtypes.
225: case LIST_PROVINCES:
226: data = PROVINCES;
227: codes = getSortedCodes(data);
228: break;
229: // STATEPROV and PROVSTATE are a bit more work; catenate the lists but do not sort the names
230: case LIST_STATEPROV:
231: data = a2dCat(STATES, PROVINCES, false);
232: codes = a1dCat(getSortedCodes(STATES),
233: getSortedCodes(PROVINCES));
234: break;
235: case LIST_PROVSTATE:
236: data = a2dCat(STATES, PROVINCES, false);
237: codes = a1dCat(getSortedCodes(PROVINCES),
238: getSortedCodes(STATES));
239: break;
240: // SORTED is a bit more work too; catenate AND sort the lists
241: case LIST_CANUSA:
242: data = a2dCat(STATES, PROVINCES, true);
243: codes = getSortedCodes(data);
244: break;
245: // COUNTRIES is relatively easy
246: case LIST_COUNTRIES:
247: data = COUNTRIES;
248: codes = getSortedCodes(COUNTRIES);
249: codesonly = false; // Country Codes only is nonsensical
250: break;
251: }
252:
253: // Now we finally have the data, add it to the component..
254: if (!codesonly) {
255: for (int i = 0; i < data.length; i++) {
256: _state.addOption(data[i][0], data[i][1]);
257: }
258: } else {
259: for (int i = 0; i < codes.length; i++) {
260: _state.addOption(codes[i], codes[i]);
261: }
262: }
263: }
264:
265: /** Catenate two arrays into one
266: * @param strings An array of Strings
267: * @param strings2 Another array of Strings
268: * @return The concatenated array.
269: */
270: private String[] a1dCat(String[] a1, String[] a2) {
271: int newLength = a1.length + a2.length;
272: String[] result = new String[newLength];
273: System.arraycopy(a1, 0, result, 0, a1.length);
274: System.arraycopy(a2, 0, result, a1.length, a2.length);
275: return result;
276: }
277:
278: /** Build a 2D array by merging two arrays.
279: * @param aa1 One of the two arrays.
280: * @param aa2 The other array.
281: * @return The merged array
282: */
283: private String[][] a2dCat(String[][] aa1, String[][] aa2,
284: boolean sort) {
285: int newLength = aa1.length + aa2.length;
286: String[][] result = new String[newLength][];
287: System.arraycopy(aa1, 0, result, 0, aa1.length);
288: System.arraycopy(aa2, 0, result, aa1.length, aa2.length);
289: if (sort) {
290: Arrays.sort(result, listComparator);
291: }
292: return result;
293: }
294:
295: /**Compare entry names in one of the countries list, for sorting. Sorts by name, which is [1], not codes
296: * which would be [0], on each row of the list.
297: */
298: static final Comparator listComparator = new Comparator() {
299: public int compare(Object o1, Object o2) {
300: String s1 = ((String[]) o1)[1];
301: String s2 = ((String[]) o2)[1];
302: return s1.compareTo(s2);
303: }
304: };
305:
306: /** The name says it all. */
307: public void generateHTML(java.io.PrintWriter p, int row)
308: throws Exception {
309: // It is essential to call getValue() here because there may be a ValueChanged
310: // event in the queue for this object, which needs to be removed, which getValue()
311: // does. The secondary calls to getValue() from the container will not
312: // do this because there are no events associated with them.
313: String val = getValue(row, true);
314: if (val == null) {
315: _state.setValue(null, row);
316: } else {
317: _state.setValue(val);
318: }
319: _state.generateHTML(p, row);
320: if (_visible && _enabled)
321: p.println("");
322: }
323:
324: /**
325: * Returns the sub-component to be used for setting focus.
326: * @return com.salmonllc.html.HtmlComponent
327: */
328: public HtmlComponent getFocus() {
329: return _state;
330: }
331:
332: /**Get the sorted list of codes from one of the country lists */
333: private final static String[] getSortedCodes(String[][] list) {
334: String[] codes = new String[list.length];
335: for (int i = 0; i < list.length; i++) {
336: codes[i] = list[i][0];
337: }
338: Arrays.sort(codes);
339: return codes;
340: }
341:
342: public boolean processParms(Hashtable parms, int rowNo)
343: throws Exception {
344:
345: if (!getVisible() || !getEnabled())
346: return false;
347:
348: // Determine the old value from both edit fields.
349:
350: String oldValue;
351: if (_dsBuff == null) {
352: oldValue = _state.getValue();
353: } else {
354: String s = null;
355: if (rowNo > -1)
356: s = (String) _dsBuff.getString(rowNo, _dsColNo);
357: else if (_dsBuff.getRow() != -1)
358: s = (String) _dsBuff.getString(_dsColNo);
359: if (s == null)
360: oldValue = null;
361: else
362: oldValue = s;
363: }
364:
365: // Determine the new value from both edit fields.
366:
367: String newValue;
368: String name1 = _state.getFullName();
369: if (rowNo > -1)
370: name1 += "_" + rowNo;
371: String val[] = (String[]) parms.get(name1);
372: if (val != null) {
373: newValue = val[0].trim();
374: if (newValue.equals(""))
375: newValue = null;
376: } else
377: newValue = null;
378:
379: // Compare old and new values and possibly create a ValueChangedEvent.
380: if (!valuesEqual(oldValue, newValue)) {
381: ValueChangedEvent e = new ValueChangedEvent(getPage(),
382: this , getName(), getFullName(), oldValue, newValue,
383: rowNo, _dsColNo, _dsBuff);
384: addEvent(e);
385: }
386: return false;
387: }
388:
389: /**
390: * This method will clear all pending events from the event queue for this component.
391: */
392: public void reset() {
393: super .reset();
394: _state.reset();
395: }
396:
397: /**
398: * Specifies the Style Class to be used for the State Component.
399: * Creation date: (7/19/01 8:41:20 AM)
400: * @param sClass java.lang.String A name of a class in Html to be used by this component
401: */
402: public void setClassName(String sClass) {
403: super .setClassName(sClass);
404: if (sClass != null)
405: _state.setClassName(sClass);
406: }
407:
408: /**
409: * Sets the font end tag for disabled mode.
410: * @param tag java.lang.String
411: */
412: public void setDisabledFontEndTag(String tag) {
413: _state.setDisabledFontEndTag(tag);
414:
415: }
416:
417: /**
418: * Sets the font tag for disabled mode.
419: * @param tag java.lang.String
420: */
421: public void setDisabledFontStartTag(String tag) {
422:
423: _state.setDisabledFontStartTag(tag);
424:
425: }
426:
427: /**
428: * Enables or disables the ability of this component to respond to user input.
429: * @param editable boolean
430: */
431: public void setEnabled(boolean enabled) {
432: super .setEnabled(enabled);
433: _state.setEnabled(enabled);
434: }
435:
436: /**
437: * Specifies the parent component for this State Component.
438: * Creation date: (7/19/01 8:41:20 AM)
439: * @param sClass java.lang.String A name of a class in Html to be used by this component
440: */
441: public void setParent(HtmlComponent parent) {
442: super .setParent(parent);
443: // This is necessary for the name to be generated correctly, else the leading
444: // sequence of parent names will be lost.
445: _state.setParent(parent);
446: }
447:
448: /**
449: * This method sets the property theme for the component.
450: * @param theme The theme to use.
451: */
452: public void setTheme(String sTheme) {
453: if (sTheme == null)
454: return;
455:
456: if (_state != null)
457: _state.setTheme(sTheme);
458: }
459:
460: /**
461: * @returns the tab index html attribute
462: */
463: public int getTabIndex() {
464: if (_tabIndex == null)
465: return -1;
466: return _tabIndex.intValue();
467: }
468:
469: /**
470: * @param sets the tab index html attribute. You can also pass TAB_INDEX_DEFAULT to use the default tab index for the component or TAB_INDEX_NONE to keep this component from being tabbed to
471: */
472: public void setTabIndex(int val) {
473: _state.setTabIndex(val + 3);
474:
475: }
476:
477: /* (non-Javadoc)
478: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
479: */
480: public int compare(Object o1, Object o2) {
481: // TODO Auto-generated method stub
482: return 0;
483: }
484: }
|