/* * Copyright (c) 2007, Dennis M. Sosnoski All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jibx.binding.generator; import java.util.Collection; import org.jibx.binding.util.NameUtilities; import org.jibx.binding.util.StringArray; import org.jibx.runtime.EnumSet; import org.jibx.runtime.IUnmarshallingContext; import org.jibx.runtime.impl.UnmarshallingContext; import org.jibx.schema.validation.ValidationContext; /** * Base class for all customizations. This defines a way to navigate up the tree of nested components without making * assumptions about the specific type of the containing components. This allows for other types of customizations, * beyond the binding customizations included directly in this package. This also includes enumeration definitions which * are used with both base and extension customizations. * * @author Dennis M. Sosnoski */ public class CustomBase { // name style value set information public static final int CAMEL_CASE_NAMES = 0; public static final int UPPER_CAMEL_CASE_NAMES = 1; public static final int HYPHENATED_NAMES = 2; public static final int DOTTED_NAMES = 3; public static final int UNDERSCORED_NAMES = 4; public static final EnumSet s_nameStyleEnum = new EnumSet(CAMEL_CASE_NAMES, new String[] { "camel-case", "upper-camel-case", "hyphenated", "dotted", "underscored" }); // require value set information public static final int REQUIRE_NONE = 0; public static final int REQUIRE_PRIMITIVES = 1; public static final int REQUIRE_OBJECTS = 2; public static final int REQUIRE_ALL = 3; public static final EnumSet s_requireEnum = new EnumSet(REQUIRE_NONE, new String[] { "none", "primitives", "objects", "all" }); // derive-namespace value set information public static final int DERIVE_NONE = 0; public static final int DERIVE_BY_PACKAGE = 1; public static final int DERIVE_FIXED = 2; public static final EnumSet s_namespaceStyleEnum = new EnumSet(DERIVE_NONE, new String[] { "none", "package", "fixed" }); // parent element (null if none) - would be final, except for unmarshalling private SharedNestingBase m_parent; /** * Constructor. * * @param parent */ public CustomBase(SharedNestingBase parent) { m_parent = parent; } /** * Get container. * * @return container */ public SharedNestingBase getParent() { return m_parent; } /** * Get global customizations root. * * @return global customization */ public GlobalCustom getGlobal() { CustomBase parent = m_parent; while (!(parent instanceof GlobalCustom)) { parent = parent.getParent(); } return (GlobalCustom)parent; } /** * Convert class, method, or parameter name to XML name. * * @param base class or simple field name to be converted * @param code conversion format style code * @return XML name */ public static String convertName(String base, int code) { // strip off trailing array indication while (base.endsWith("[]")) { base = base.substring(0, base.length() - 2); } // skip any leading special characters in name int length = base.length(); int offset = -1; char chr = 0; while (++offset < length && ((chr = base.charAt(offset)) == '_' || (chr == '$'))); if (offset >= length) { return "_"; } else { // make sure valid first character of name StringBuffer buff = new StringBuffer(); if (Character.isDigit(chr)) { buff.append('_'); } // scan for word splits in supplied name boolean split = true; boolean caps = false; while (offset < length) { // split if underscore or change to upper case, or upper ending chr = base.charAt(offset++); boolean nextlower = offset < length && !Character.isUpperCase(base.charAt(offset)); if (chr == '_') { split = true; continue; } else if (Character.isUpperCase(chr)) { if (!caps || nextlower) { split = true; } if (code != CAMEL_CASE_NAMES && code != UPPER_CAMEL_CASE_NAMES) { chr = Character.toLowerCase(chr); } } if (split) { // convert word break caps = !nextlower; boolean tolower = false; char separator = 0; switch (code) { case CAMEL_CASE_NAMES: { if (buff.length() != 0) { chr = Character.toUpperCase(chr); tolower = false; } else { tolower = nextlower || offset == length; } break; } case UPPER_CAMEL_CASE_NAMES: { tolower = false; chr = Character.toUpperCase(chr); break; } case HYPHENATED_NAMES: { separator = '-'; break; } case DOTTED_NAMES: { separator = '.'; break; } case UNDERSCORED_NAMES: { separator = '_'; break; } } if (separator > 0 && buff.length() > 0) { buff.append(separator); } if (tolower) { chr = Character.toLowerCase(chr); } split = false; } if (chr != '$') { // no split, just append the character buff.append(chr); } } return buff.toString(); } } /** * Derive name for item in a collection. If the supplied collection name ends in a recognized plural form the * derived item name is the singular version of the collection name. Otherwise, it is the converted name of the * collection item class, or just "item" if the class is unknown. TODO: internationalization? * * @param cname collection name * @param type item type (null if unknown) * @param code conversion format style code * @return item name */ public static String deriveItemName(String cname, String type, int code) { String name = NameUtilities.depluralize(cname); if (!name.equals(cname)) { return name; } else if (type != null && !"java.lang.Object".equals(type)) { return convertName(type.substring(type.lastIndexOf('.') + 1), code); } else { return "item"; } } /** * Get the package from a fully-qualified type name. * * @param type fully-qualified type name * @return package of the type (empty string if in default package) */ public static String packageOfType(String type) { int split = type.lastIndexOf('.'); if (split >= 0) { return type.substring(0, split); } else { return ""; } } /** * Create a namespace URL from a package path. * * @param pkgpth fully-qualified package name * @return namespace based on package (null if none) */ public static String packageToNamespace(String pkgpth) { int mark = pkgpth.indexOf('.'); if (mark >= 0) { StringBuffer buff = new StringBuffer(); buff.append("http://"); String comp = pkgpth.substring(0, mark); int base = mark + 1; char delim = '.'; if ("com".equals(comp) || "net".equals(comp) || "org".equals(comp)) { mark = pkgpth.indexOf('.', base); if (mark > 0) { buff.append(pkgpth.substring(base, mark)); } else { buff.append(pkgpth.substring(base)); } buff.append('.'); base = mark + 1; delim = '/'; } buff.append(comp); while (mark > 0) { buff.append(delim); base = mark + 1; mark = pkgpth.indexOf('.', base); if (mark > 0) { buff.append(pkgpth.substring(base, mark)); } else { buff.append(pkgpth.substring(base)); } } return buff.toString(); } else { return null; } } /** * Derive namespace using specified technique. * * @param uri base namespace URI (null if none) * @param pkgpth fully qualified package name * @param style namespace style code * @return derived namespace */ public static String deriveNamespace(String uri, String pkgpth, int style) { switch (style) { case DERIVE_NONE: return null; case DERIVE_FIXED: return uri; case DERIVE_BY_PACKAGE: { if (uri == null) { return packageToNamespace(pkgpth); } else if (pkgpth == null) { return uri; } else { // append the last package to passed URI String pack; int start = pkgpth.lastIndexOf('.'); if (start >= 0) { pack = pkgpth.substring(start + 1); } else { pack = pkgpth; } if (uri.endsWith("/")) { return uri + pack; } else { return uri + '/' + pack; } } } default: throw new IllegalStateException("Invalid style code"); } } /** * Validate attributes of element. This is designed to be called during unmarshalling as part of the pre-set method * processing when a subclass instance is being created. * * @param ictx unmarshalling context * @param attrs attributes array */ protected void validateAttributes(IUnmarshallingContext ictx, StringArray attrs) { // setup for attribute access ValidationContext vctx = (ValidationContext)ictx.getUserContext(); UnmarshallingContext uctx = (UnmarshallingContext)ictx; // loop through all attributes of current element for (int i = 0; i < uctx.getAttributeCount(); i++) { // check if nonamespace attribute is in the allowed set String name = uctx.getAttributeName(i); if (uctx.getAttributeNamespace(i).length() == 0) { if (attrs.indexOf(name) < 0) { vctx.addWarning("Undefined attribute " + name, this); } } } } /** * Gets the parent element link from the unmarshalling stack. This method is for use by factories during * unmarshalling. * * @param ictx unmarshalling context * @return containing class */ protected static Object getContainingObject(IUnmarshallingContext ictx) { Object parent = ictx.getStackTop(); if (parent instanceof Collection) { parent = ictx.getStackObject(1); } return parent; } }