001: /*
002: Copyright (c) 2007, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.generator;
030:
031: import java.util.HashSet;
032: import java.util.Set;
033:
034: import org.jibx.runtime.EnumSet;
035:
036: /**
037: * Base class for all customizations. This defines a way to navigate up the tree
038: * of nested components without making assumptions about the specific type of
039: * the containing components. This allows for other types of customizations,
040: * beyond the binding customizations included directly in this package. This
041: * also includes enumeration definitions which are used with both base and
042: * extension customizations.
043: */
044: public class CustomBase {
045: // name style value set information
046: public static final int CAMEL_CASE_NAMES = 0;
047: public static final int UPPER_CAMEL_CASE_NAMES = 1;
048: public static final int HYPHENATED_NAMES = 2;
049: public static final int DOTTED_NAMES = 3;
050: public static final int UNDERSCORED_NAMES = 4;
051:
052: public static final EnumSet s_nameStyleEnum = new EnumSet(
053: CAMEL_CASE_NAMES, new String[] { "camel-case",
054: "upper-camel-case", "hyphenated", "dotted",
055: "underscored" });
056:
057: // require value set information
058: public static final int REQUIRE_NONE = 0;
059: public static final int REQUIRE_PRIMITIVES = 1;
060: public static final int REQUIRE_OBJECTS = 2;
061: public static final int REQUIRE_ALL = 3;
062:
063: public static final EnumSet s_requireEnum = new EnumSet(
064: REQUIRE_NONE, new String[] { "none", "primitives",
065: "objects", "all" });
066:
067: // derive-namespace value set information
068: public static final int DERIVE_NONE = 0;
069: public static final int DERIVE_BY_PACKAGE = 1;
070: public static final int DERIVE_FIXED = 2;
071:
072: public static final EnumSet s_namespaceStyleEnum = new EnumSet(
073: DERIVE_NONE, new String[] { "none", "package", "fixed" });
074:
075: // parent element (null if none) - would be final, except for unmarshalling
076: private SharedNestingBase m_parent;
077:
078: /**
079: * Constructor.
080: *
081: * @param parent
082: */
083: public CustomBase(SharedNestingBase parent) {
084: m_parent = parent;
085: }
086:
087: /**
088: * Get container.
089: *
090: * @return container
091: */
092: public SharedNestingBase getParent() {
093: return m_parent;
094: }
095:
096: /**
097: * Get global customizations root.
098: *
099: * @return global customization
100: */
101: public GlobalCustom getGlobal() {
102: CustomBase parent = m_parent;
103: while (!(parent instanceof GlobalCustom)) {
104: parent = parent.getParent();
105: }
106: return (GlobalCustom) parent;
107: }
108:
109: /**
110: * Utility method to build a set from an array of names.
111: *
112: * @param names (<code>null</code> if none)
113: * @return name set (<code>null</code> if none)
114: */
115: protected static Set nameSet(String[] names) {
116: if (names == null) {
117: return null;
118: } else {
119: HashSet set = new HashSet();
120: for (int i = 0; i < names.length; i++) {
121: set.add(names[i].toLowerCase());
122: }
123: return set;
124: }
125: }
126:
127: /**
128: * Convert class, method, or parameter name to XML name.
129: *
130: * @param base class or simple field name to be converted
131: * @param code conversion format style code
132: * @return XML name
133: */
134: public static String convertName(String base, int code) {
135:
136: // strip off trailing array indication
137: while (base.endsWith("[]")) {
138: base = base.substring(0, base.length() - 2);
139: }
140:
141: // skip any leading special characters in name
142: int length = base.length();
143: int offset = -1;
144: char chr = 0;
145: while (++offset < length
146: && ((chr = base.charAt(offset)) == '_' || (chr == '$')))
147: ;
148: if (offset >= length) {
149: return "_";
150: } else {
151:
152: // make sure valid first character of name
153: StringBuffer buff = new StringBuffer();
154: if (Character.isDigit(chr)) {
155: buff.append('_');
156: }
157:
158: // scan for word splits in supplied name
159: boolean split = true;
160: boolean caps = false;
161: while (offset < length) {
162:
163: // split if underscore or change to upper case, or upper ending
164: chr = base.charAt(offset++);
165: boolean nextlower = offset < length
166: && !Character.isUpperCase(base.charAt(offset));
167: if (chr == '_') {
168: split = true;
169: continue;
170: } else if (Character.isUpperCase(chr)) {
171: if (!caps || nextlower) {
172: split = true;
173: }
174: }
175: if (split) {
176:
177: // convert word break
178: caps = !nextlower;
179: boolean tolower = nextlower || offset == length;
180: char separator = 0;
181: switch (code) {
182:
183: case CAMEL_CASE_NAMES: {
184: if (buff.length() != 0) {
185: chr = Character.toUpperCase(chr);
186: tolower = false;
187: }
188: break;
189: }
190:
191: case UPPER_CAMEL_CASE_NAMES: {
192: tolower = false;
193: chr = Character.toUpperCase(chr);
194: break;
195: }
196:
197: case HYPHENATED_NAMES: {
198: separator = '-';
199: break;
200: }
201:
202: case DOTTED_NAMES: {
203: separator = '.';
204: break;
205: }
206:
207: case UNDERSCORED_NAMES: {
208: separator = '_';
209: break;
210: }
211:
212: }
213: if (separator > 0 && buff.length() > 0) {
214: buff.append(separator);
215: }
216: if (tolower) {
217: chr = Character.toLowerCase(chr);
218: }
219: split = false;
220:
221: }
222: if (chr != '$') {
223:
224: // no split, just append the character
225: buff.append(chr);
226:
227: }
228: }
229: return buff.toString();
230: }
231: }
232:
233: /**
234: * Derive name for item in a collection. If the supplied collection name
235: * ends in a recognized plural form the derived item name is the singular
236: * version of the collection name. Otherwise, it is the converted name of
237: * the collection item class, or just "item" if the class is unknown.
238: *
239: * @param cname collection name
240: * @param type item type (<code>null</code> if unknown)
241: * @param code conversion format style code
242: * @return item name
243: */
244: public static String deriveItemName(String cname, String type,
245: int code) {
246: if (cname.endsWith("ies")) {
247: return cname.substring(0, cname.length() - 3) + 'y';
248: } else if (cname.endsWith("sses")) {
249: return cname.substring(0, cname.length() - 2);
250: } else if (cname.endsWith("s")) {
251: return cname.substring(0, cname.length() - 1);
252: } else if (type != null && !"java.lang.Object".equals(type)) {
253: return convertName(type
254: .substring(type.lastIndexOf('.') + 1), code);
255: } else {
256: return "item";
257: }
258: }
259:
260: /**
261: * Get the package from a fully-qualified type name.
262: *
263: * @param type fully-qualified type name
264: * @return package of the type (empty string if in default package)
265: */
266: public static String packageOfType(String type) {
267: int split = type.lastIndexOf('.');
268: if (split >= 0) {
269: return type.substring(0, split);
270: } else {
271: return "";
272: }
273: }
274:
275: /**
276: * Create a namespace URL from a package path.
277: *
278: * @param pkgpth fully-qualified package name
279: * @return namespace based on package (<code>null</code> if none)
280: */
281: public static String packageToNamespace(String pkgpth) {
282: int mark = pkgpth.indexOf('.');
283: if (mark >= 0) {
284: StringBuffer buff = new StringBuffer();
285: buff.append("http://");
286: String comp = pkgpth.substring(0, mark);
287: int base = mark + 1;
288: char delim = '.';
289: if ("com".equals(comp) || "net".equals(comp)
290: || "org".equals(comp)) {
291: mark = pkgpth.indexOf('.', base);
292: if (mark > 0) {
293: buff.append(pkgpth.substring(base, mark));
294: } else {
295: buff.append(pkgpth.substring(base));
296: }
297: buff.append('.');
298: base = mark + 1;
299: delim = '/';
300: }
301: buff.append(comp);
302: while (mark > 0) {
303: buff.append(delim);
304: base = mark + 1;
305: mark = pkgpth.indexOf('.', base);
306: if (mark > 0) {
307: buff.append(pkgpth.substring(base, mark));
308: } else {
309: buff.append(pkgpth.substring(base));
310: }
311: }
312: return buff.toString();
313: } else {
314: return null;
315: }
316: }
317:
318: /**
319: * Derive namespace using specified technique.
320: *
321: * @param uri base namespace URI (<code>null</code> if none)
322: * @param pkgpth fully qualified package name
323: * @param style namespace style code
324: * @return derived namespace
325: */
326: public static String deriveNamespace(String uri, String pkgpth,
327: int style) {
328: switch (style) {
329: case DERIVE_NONE:
330: return null;
331:
332: case DERIVE_FIXED:
333: return uri;
334:
335: case DERIVE_BY_PACKAGE: {
336: if (uri == null) {
337: return packageToNamespace(pkgpth);
338: } else if (pkgpth == null) {
339: return uri;
340: } else {
341:
342: // append the last package to passed URI
343: String pack;
344: int start = pkgpth.lastIndexOf('.');
345: if (start >= 0) {
346: pack = pkgpth.substring(start + 1);
347: } else {
348: pack = pkgpth;
349: }
350: if (uri.endsWith("/")) {
351: return uri + pack;
352: } else {
353: return uri + '/' + pack;
354: }
355: }
356: }
357:
358: default:
359: throw new IllegalStateException("Invalid style code");
360: }
361: }
362: }
|