/*
* Copyright (c) 2007-2008, 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.schema.codegen;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.eclipse.jdt.core.dom.AST;
import org.jibx.binding.model.BindingDirectory;
import org.jibx.binding.model.BindingHolder;
import org.jibx.binding.model.ElementBase;
import org.jibx.binding.model.FormatElement;
import org.jibx.binding.model.MappingElement;
import org.jibx.binding.model.StructureElement;
import org.jibx.extras.DocumentComparator;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;
import org.jibx.runtime.QName;
import org.jibx.runtime.impl.UnmarshallingContext;
import org.jibx.schema.INamed;
import org.jibx.schema.SchemaUtils;
import org.jibx.schema.SchemaVisitor;
import org.jibx.schema.TreeWalker;
import org.jibx.schema.UrlResolver;
import org.jibx.schema.codegen.custom.ComponentExtension;
import org.jibx.schema.codegen.custom.GlobalExtension;
import org.jibx.schema.codegen.custom.SchemaCustom;
import org.jibx.schema.codegen.custom.SchemasetCustom;
import org.jibx.schema.elements.AnnotatedBase;
import org.jibx.schema.elements.CommonTypeDerivation;
import org.jibx.schema.elements.ComplexTypeElement;
import org.jibx.schema.elements.ElementElement;
import org.jibx.schema.elements.FilteredSegmentList;
import org.jibx.schema.elements.OpenAttrBase;
import org.jibx.schema.elements.SchemaBase;
import org.jibx.schema.elements.SchemaElement;
import org.jibx.schema.elements.SchemaLocationBase;
import org.jibx.schema.elements.SimpleRestrictionElement;
import org.jibx.schema.elements.UnionElement;
import org.jibx.schema.support.LazyList;
import org.jibx.schema.support.SchemaTypes;
import org.jibx.schema.validation.PrevalidationVisitor;
import org.jibx.schema.validation.RegistrationVisitor;
import org.jibx.schema.validation.ValidationContext;
import org.jibx.schema.validation.ValidationProblem;
import org.jibx.schema.validation.ValidationVisitor;
import org.jibx.util.InsertionOrderedSet;
import org.xmlpull.v1.XmlPullParserException;
/**
* Code generator from schema definition.
*/
public class CodeGenerator
{
/** Logger for class. */
private static final Logger s_logger = Logger.getLogger(CodeGenerator.class.getName());
/** Default type replacements applied. */
private static final QName[] DEFAULT_REPLACEMENTS =
new QName[] {
SchemaTypes.ANY_URI.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.DURATION.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.ENTITIES.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.ENTITY.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.GDAY.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.GMONTH.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.GMONTHDAY.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.GYEAR.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.GYEARMONTH.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.ID.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.IDREF.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.IDREFS.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.LANGUAGE.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NAME.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NEGATIVE_INTEGER.getQName(), SchemaTypes.INTEGER.getQName(),
SchemaTypes.NON_NEGATIVE_INTEGER.getQName(), SchemaTypes.INTEGER.getQName(),
SchemaTypes.NON_POSITIVE_INTEGER.getQName(), SchemaTypes.INTEGER.getQName(),
SchemaTypes.NORMALIZED_STRING.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NCNAME.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NMTOKEN.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NMTOKENS.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.NOTATION.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.POSITIVE_INTEGER.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.TOKEN.getQName(), SchemaTypes.STRING.getQName(),
SchemaTypes.UNSIGNED_BYTE.getQName(), SchemaTypes.BYTE.getQName(),
SchemaTypes.UNSIGNED_INT.getQName(), SchemaTypes.INT.getQName(),
SchemaTypes.UNSIGNED_LONG.getQName(), SchemaTypes.LONG.getQName(),
SchemaTypes.UNSIGNED_SHORT.getQName(), SchemaTypes.SHORT.getQName()
};
/** Mask for schema elements which derive from a type. */
private static final long TYPE_DERIVE_MASK =
SchemaBase.ELEMENT_MASKS[SchemaBase.EXTENSION_TYPE] | SchemaBase.ELEMENT_MASKS[SchemaBase.RESTRICTION_TYPE];
/** Mask for schema elements which define a type. */
private static final long TYPE_DEFINE_MASK =
SchemaBase.ELEMENT_MASKS[SchemaBase.COMPLEXTYPE_TYPE] | SchemaBase.ELEMENT_MASKS[SchemaBase.SIMPLETYPE_TYPE];
/** Mask for schema elements which block name inheritance downward. */
private static final long BLOCK_NAME_INHERIT_MASK =
TYPE_DERIVE_MASK | SchemaBase.ELEMENT_MASKS[SchemaBase.UNION_TYPE];
/** Code generation customizations. */
private final SchemasetCustom m_global;
/** Root URL for schemas. */
private final URL m_schemaRoot;
/** Root directory for schemas. */
private final File m_schemaDir;
/** Target directory for code generation. */
private final File m_targetDir;
/** Context for loading and processing schemas. */
private final ValidationContext m_validationContext;
/** Package directory for generated classes. */
private PackageDirectory m_packageDirectory;
/** Definitions to be generated (may be global schema definitions, or reused nested components with classes). */
private ArrayList m_definitions;
/** Directory for constructed bindings. */
private BindingDirectory m_bindingDirectory;
/**
* Constructor.
*
* @param parms command line parameters
*/
public CodeGenerator(CodeGeneratorCommandLine parms) {
m_global = parms.getCustomRoot();
m_global.setSubstitutions(DEFAULT_REPLACEMENTS);
m_schemaRoot = parms.getSchemaRoot();
m_schemaDir = parms.getSchemaDir();
m_targetDir = parms.getGeneratePath();
m_validationContext = new ValidationContext();
}
/**
* Constructor used by tests. This uses supplied schemas and skips writing to the file system.
*
* @param custom
* @param vctx
*/
public CodeGenerator(SchemasetCustom custom, ValidationContext vctx) {
m_global = custom;
m_global.setSubstitutions(DEFAULT_REPLACEMENTS);
m_schemaRoot = null;
m_schemaDir = null;
m_targetDir = null;
m_validationContext = vctx;
}
/**
* Find the most specific schemaset owning a schema. If multiple matches are found which are not in line of
* containment the first match is returned and the conflict is reported as an error.
*
* @param schema
* @param custom schema set customization
* @return owning schemaset, null
if none
*/
private SchemasetCustom findSchemaset(SchemaElement schema, SchemasetCustom custom) {
LazyList childs = custom.getChildren();
SchemasetCustom owner = null;
String name = schema.getResolver().getName();
for (int i = 0; i < childs.size(); i++) {
Object child = childs.get(i);
if (child instanceof SchemasetCustom) {
SchemasetCustom schemaset = (SchemasetCustom)child;
if (schemaset.isInSet(name, schema)) {
SchemasetCustom match = findSchemaset(schema, schemaset);
if (match != null) {
if (owner == null) {
owner = match;
} else {
m_validationContext.addError("schema-set overlap on schema " + name + " (first match "
+ ValidationProblem.componentDescription(owner) + ')', match);
}
}
}
}
}
return owner == null ? custom : owner;
}
/**
* Validate the schemas.
*
* @param schemas schemas to be validated
*/
public void validateSchemas(SchemaElement[] schemas) {
// validate the schemas and report any problems
TreeWalker wlkr = new TreeWalker(m_validationContext, m_validationContext);
s_logger.debug("Beginning schema prevalidation pass");
m_validationContext.clearTraversed();
s_logger.debug("Beginning schema prevalidation pass");
for (int i = 0; i < schemas.length; i++) {
wlkr.walkSchema(schemas[i], new PrevalidationVisitor(m_validationContext));
System.out.println("After prevalidation schema " + schemas[i].getResolver().getName() +
" has effective namespace " + schemas[i].getEffectiveNamespace());
}
s_logger.debug("Beginning schema registration pass");
m_validationContext.clearTraversed();
for (int i = 0; i < schemas.length; i++) {
wlkr.walkSchema(schemas[i], new RegistrationVisitor(m_validationContext));
}
s_logger.debug("Beginning validation pass");
m_validationContext.clearTraversed();
for (int i = 0; i < schemas.length; i++) {
wlkr.walkSchema(schemas[i], new ValidationVisitor(m_validationContext));
System.out.println("After validation schema " + schemas[i].getResolver().getName() +
" has effective namespace " + schemas[i].getEffectiveNamespace());
}
}
/**
* Load and validate the root schema list.
*
* @param list resolvers for schemas to be loaded
* @return schemas in validation order
* @throws JiBXException on unrecoverable error in schemas
* @throws IOException on error reading schemas
*/
private SchemaElement[] load(ArrayList list) throws JiBXException, IOException {
IBindingFactory factory = org.jibx.runtime.BindingDirectory.getFactory(SchemaElement.class);
IUnmarshallingContext ictx = factory.createUnmarshallingContext();
int count = list.size();
SchemaElement[] schemas = new SchemaElement[count];
for (int i = 0; i < count; i++) {
// unmarshal document to construct schema structure
UrlResolver resolver = (UrlResolver)list.get(i);
ictx.setDocument(resolver.getContent(), resolver.getName(), null);
ictx.setUserContext(m_validationContext);
Object obj = ictx.unmarshalElement();
// set resolver for use during schema processing
SchemaElement schema = (SchemaElement)obj;
schemas[i] = schema;
schema.setResolver(resolver);
String id = resolver.getId();
m_validationContext.setSchema(id, schema);
// verify schema roundtripping if debug enabled
if (s_logger.isDebugEnabled()) {
try {
// determine encoding of input document
String enc = ((UnmarshallingContext)ictx).getInputEncoding();
if (enc == null) {
enc = "UTF-8";
}
// marshal root object back out to document in memory
IMarshallingContext mctx = factory.createMarshallingContext();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
mctx.setIndent(2);
mctx.marshalDocument(obj, "UTF-8", null, bos);
// compare with original input document
InputStreamReader brdr =
new InputStreamReader(new ByteArrayInputStream(bos.toByteArray()), "UTF-8");
InputStreamReader frdr = new InputStreamReader(resolver.getContent(), enc);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream pstream = new PrintStream(baos);
DocumentComparator comp = new DocumentComparator(pstream);
if (comp.compare(frdr, brdr)) {
// report schema roundtripped successfully
s_logger.debug("Successfully roundtripped schema " + id);
} else {
// report problems in roundtripping schema
s_logger.debug("Errors in roundtripping schema " + id);
pstream.flush();
s_logger.debug(baos.toString());
}
} catch (XmlPullParserException e) {
s_logger.debug("Error during schema roundtripping", e);
}
}
}
// to correctly handle namespaces for includes, process namespaced schemas first
Set schemaset = new HashSet(count);
ArrayList ordereds = new ArrayList();
m_validationContext.clearTraversed();
for (int i = 0; i < count; i++) {
SchemaElement schema = schemas[i];
if (schema.getTargetNamespace() != null) {
// add namespaced schema to both reached set and processing order list
ordereds.add(schema);
schemaset.add(schema);
// add any child include and imports only to reached set
FilteredSegmentList childs = schema.getSchemaChildren();
for (int j = 0; j < childs.size(); j++) {
Object child = childs.get(j);
if (child instanceof SchemaLocationBase) {
schemaset.add(((SchemaLocationBase)child).getReferencedSchema());
}
}
}
}
// add any schemas not already covered
for (int i = 0; i < count; i++) {
SchemaElement schema = schemas[i];
if (!schemaset.contains(schema)) {
ordereds.add(schema);
}
}
// add element definitions to schema if requested
if (ClassHolder.ADD_GLOBAL_ELEMENTS) {
for (int i = 0; i < ordereds.size(); i++) {
SchemaElement schema = (SchemaElement)ordereds.get(i);
ArrayList elementdefs = new ArrayList();
FilteredSegmentList childs = schema.getTopLevelChildren();
for (int j = 0; j < childs.size(); j++) {
SchemaBase child = (SchemaBase)childs.get(j);
if (child.type() == SchemaBase.COMPLEXTYPE_TYPE) {
ElementElement elementdef = new ElementElement();
String name = ((ComplexTypeElement)child).getName();
elementdef.setType(new QName(schema.getTargetNamespace(), name));
elementdef.setName(name);
elementdefs.add(elementdef);
}
}
if (elementdefs.size() > 0) {
childs.addAll(elementdefs);
s_logger.debug("Added " + elementdefs.size() + " element definitions for complexTypes to schema " + schema.getResolver().getName());
}
}
}
// validate the schemas in order
SchemaElement[] ordschemas = (SchemaElement[])ordereds.toArray(new SchemaElement[ordereds.size()]);
validateSchemas(ordschemas);
return ordschemas;
}
/**
* Validate and apply customizations to loaded schemas.
*
* @return true
if successful, false
if error
*/
public boolean customizeSchemas() {
// TODO: remove this once union handling fully implemented
SchemaVisitor visitor = new SchemaVisitor() {
public void exit(UnionElement node) {
OpenAttrBase parent = node.getParent();
int count = parent.getChildCount();
for (int i = 0; i < count; i++) {
if (parent.getChild(i) == node) {
SimpleRestrictionElement empty = new SimpleRestrictionElement();
empty.setBase(SchemaTypes.STRING.getQName());
parent.replaceChild(i, empty);
break;
}
}
}
};
TreeWalker wlkr = new TreeWalker(null, null);
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
wlkr.walkElement((SchemaElement)iter.next(), visitor);
}
// validate the customizations
m_global.validate(m_validationContext);
// create the package directory, using default package from root schemaset
String dfltpack = m_global.getPackage();
if (dfltpack == null) {
dfltpack = "";
}
m_packageDirectory = new PackageDirectory(m_targetDir, dfltpack);
// link each schema to a customization, creating a default customization if necessary
int count = 0;
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
s_logger.debug("Assigning customization for schema " + ++count + ": " + schema.getResolver().getName());
SchemasetCustom owner = findSchemaset(schema, m_global);
SchemaCustom custom = owner.forceCustomization(schema.getResolver().getName(), schema, m_validationContext);
custom.validate(m_validationContext);
NameConverter nconv = new NameConverter();
nconv.setDiscardPrefixSet(custom.getStripPrefixes());
nconv.setDiscardSuffixSet(custom.getStripSuffixes());
String pname = custom.getPackage();
PackageHolder holder = null;
if (pname == null) {
String uri = schema.getEffectiveNamespace();
if (uri == null) {
uri = "";
}
holder = m_packageDirectory.getPackageForUri(uri);
} else {
holder = m_packageDirectory.getPackage(pname);
}
custom.extend(nconv, holder, m_validationContext);
}
// check all the customizations
m_global.checkSchemas(m_validationContext);
return !reportProblems(m_validationContext);
}
/**
* Process substitutions and deletions defined by extensions. This builds the cross-reference information for the
* global definition components of the schemas while removing references to deleted components.
*
* @return true
if any changes to the schemas, false
if not
*/
private boolean processExtensions() {
// first clear all the cross reference information
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
int count = schema.getChildCount();
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
Object obj = child.getExtension();
if (obj instanceof GlobalExtension) {
((GlobalExtension)obj).resetDependencies();
}
}
}
// process each loaded schema for deletions and cross referencing
int index = 0;
m_validationContext.clearTraversed();
boolean modified = false;
// Level level = TreeWalker.setLogging(s_logger.getLevel());
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
m_validationContext.enterSchema(schema);
s_logger.debug("Applying extensions to schema " + ++index + ": " + schema.getResolver().getName());
int count = schema.getChildCount();
boolean instmod = false;
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
Object obj = child.getExtension();
if (obj instanceof GlobalExtension) {
// apply extension to global definition element
ComponentExtension exten = (ComponentExtension)obj;
if (exten.isRemoved()) {
// just eliminate this definition from the schema
schema.detachChild(i);
instmod = true;
} else {
// process the definition to remove references to deleted components
exten.applyAndCountUsage(m_validationContext);
}
}
}
if (instmod) {
schema.compactChildren();
modified = true;
}
m_validationContext.exitSchema();
}
// TreeWalker.setLogging(level);
return modified;
}
/**
* Apply extensions and normalize all schemas. This may be a multipass process, since applying extensions may create
* the opportunity for further normalizations and vice versa.
*/
public void applyAndNormalize() {
// loop until no modifications, with at least one pass of extensions and normalizations
boolean modified = true;
while (processExtensions() || modified) {
// normalize all the schema definitions
modified = false;
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
int count = schema.getChildCount();
boolean instmod = false;
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
Object obj = child.getExtension();
if (obj instanceof GlobalExtension) {
GlobalExtension global = (GlobalExtension)obj;
global.normalize();
if (global.isRemoved()) {
// just eliminate this definition from the schema
schema.detachChild(i);
instmod = true;
}
}
}
if (instmod) {
schema.compactChildren();
modified = true;
}
}
}
// finish by flagging global definitions requiring separate classes
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
SchemaCustom custom = findSchemaset(schema, m_global).getCustomization(schema.getResolver().getName());
if (custom.isGenerateUnused()) {
int count = schema.getChildCount();
for (int i = 0; i < count; i++) {
SchemaBase comp = schema.getChild(i);
if (comp.type() == SchemaBase.ELEMENT_TYPE || comp.type() == SchemaBase.COMPLEXTYPE_TYPE) {
Object obj = comp.getExtension();
if (obj instanceof GlobalExtension) {
((GlobalExtension)obj).setIncluded(true);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Set include for definition " + SchemaUtils.describeComponent(comp));
}
}
}
}
}
}
}
/**
* Processes the schemas to remove unused global definitions.
*/
public void pruneDefinitions() {
// start by recursively checking for removable global definitions
int index = 0;
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
s_logger.debug("Checking for unused definitions in schema " + ++index + ": "
+ schema.getResolver().getName());
int count = schema.getChildCount();
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
Object exten = child.getExtension();
if (exten instanceof GlobalExtension) {
// check if global definition is unused and not specifically required
((GlobalExtension)exten).checkRemovable();
}
}
}
// next remove all the definitions flagged in the first step
index = 0;
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
s_logger.debug("Deleting unused definitions in schema " + ++index + ": " + schema.getResolver().getName());
int count = schema.getChildCount();
boolean modified = false;
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
Object exten = child.getExtension();
if (exten instanceof GlobalExtension && ((ComponentExtension)exten).isRemoved()) {
// remove the definition from schema
schema.detachChild(i);
modified = true;
if (s_logger.isDebugEnabled()) {
s_logger.debug(" Removed definition " + ((INamed)child).getQName());
}
}
}
if (modified) {
schema.compactChildren();
}
}
}
/**
* Check if an item has an associated name. If the component associated with the item has a name, this just returns
* that name. The only exception is for inlined global type definitions, which are treated as unnamed.
*
* @param item
* @return name associated name, or null
if none
*/
private String checkDirectName(Item item) {
AnnotatedBase comp = item.getSchemaComponent();
if (comp instanceof INamed) {
// check for an inlined global type definition
boolean usename = true;
if (comp.isGlobal()) {
if ((comp.bit() & TYPE_DEFINE_MASK) != 0 && !(item instanceof DefinitionItem)) {
usename = false;
}
}
if (usename) {
// use name from schema component
String name = ((INamed)comp).getName();
if (name != null) {
NameConverter nconv = item.getComponentExtension().getGlobal().getNameConverter();
return nconv.toBaseName(nconv.trimXName(name));
}
}
}
return null;
}
/**
* Derive the base name for an item. If not forced, the only time a name will be returned is when the item is a
* reference to a non-type definition. If forced, this will try other alternatives for names including the text
* "Enumeration" for an enumeration group, the base type name for a type derivation, the schema type name for a
* value of a schema type, or finally the schema component element name.
*
* @param item
* @param force name forced flag
* @return name (null
if to use inherited name when force == false
)
*/
private String deriveName(Item item, boolean force) {
// try alternatives, in decreasing preference order
AnnotatedBase comp = item.getSchemaComponent();
String text = null;
if (force) {
if (item instanceof ReferenceItem) {
text = ((ReferenceItem)item).getDefinition().getName();
} else if (item instanceof GroupItem && ((GroupItem)item).isEnumeration()) {
text = "Enumeration";
} else if ((TYPE_DERIVE_MASK & comp.bit()) != 0) {
text = ((CommonTypeDerivation)comp).getBase().getName();
} else if (item instanceof ValueItem) {
text = ((ValueItem)item).getSchemaType().getName();
} else {
text = comp.name();
}
} else if (item instanceof ReferenceItem && (TYPE_DEFINE_MASK & comp.type()) == 0) {
// use name from definition in the case of anything except a type definition
text = ((ReferenceItem)item).getDefinition().getName();
}
if (text == null) {
return null;
} else {
return item.getComponentExtension().getGlobal().getNameConverter().toBaseName(text);
}
}
/**
* Compact group structures. This eliminates redundant groupings, in the form of groups with only one child, which
* child is a group referencing the same schema component as the parent group, from the data structure
* representation.
*
* @param group
*/
private void compactGroups(GroupItem group) {
Item child;
while (group.getChildCount() == 1 && (child = group.getFirstChild()) instanceof GroupItem &&
child.getSchemaComponent() == group.getSchemaComponent()) {
group.adoptChildren((GroupItem)child);
}
for (child = group.getFirstChild(); child != null; child = child.getNext()) {
if (child instanceof GroupItem) {
compactGroups((GroupItem)child);
}
}
}
/**
* Set the basic names to be used for a structure of items. For named components of the schema definition the names
* used are simply the converted XML local names, for other components more complex rules apply (see {@link
* #deriveName(Item,boolean)}. This method calls itself recursively to handle nested groups.
*
* @param group
* @param force group name forced flag
*/
private void assignNames(GroupItem group, boolean force) {
// use existing name if set, otherwise derive from context if necessary
String name = group.getName();
boolean propagate = group.getChildCount() == 1;
if (name == null) {
name = checkDirectName(group);
if (name == null && force) {
propagate = false;
name = deriveName(group, true);
}
}
// set name needed for this structure (as either value or class)
if (name != null) {
if (group.getName() == null) {
group.setName(NameConverter.toNameLead(name));
}
if (group.getClassName() == null) {
group.setClassName(NameConverter.toNameWord(name));
}
}
// propagate name downward if group is inline and single nested item without its own name
Item head = group.getFirstChild();
if (propagate) {
// name can be inherited, but continue recursion for child group
if (head instanceof GroupItem) {
assignNames((GroupItem)head, false);
}
} else {
// process all child items with definite name assignments
for (Item item = head; item != null; item = item.getNext()) {
if (item instanceof GroupItem) {
assignNames((GroupItem)item, true);
} else {
if (item.getName() == null) {
String childname = checkDirectName(item);
if (childname == null) {
childname = deriveName(item, true);
}
item.setName(NameConverter.toNameLead(childname));
}
}
}
}
}
/**
* Compute the complexity of a structure. In order to find the complexity of a structure all items of the structure
* must first be checked for inlining, which in turn requires checking their complexity. That makes this method
* mutually recursive with {@link #checkInline(DefinitionItem, int)}.
*
* @param group
* @param depth nesting depth
* @return complexity (0, 1, or 2 for anything more than a single value)
*/
private int computeComplexity(GroupItem group, int depth) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(SchemaUtils.getIndentation(depth) + "counting values for "
+ (group instanceof DefinitionItem ? "definition " : "group ")
+ SchemaUtils.describeComponent(group.getSchemaComponent()));
}
// count the actual values in the structure
int count = 0;
for (Item item = group.getFirstChild(); item != null; item = item.getNext()) {
// handle inlining of references
if (item instanceof ReferenceItem) {
// first make sure the definition has been checked for inlining
ReferenceItem reference = (ReferenceItem)item;
DefinitionItem definition = reference.getDefinition();
checkInline(definition, depth + 1);
if (definition.isInline()) {
// convert the reference to an inline copy of the definition
item = reference.inlineReference();
if (s_logger.isDebugEnabled()) {
s_logger.debug(SchemaUtils.getIndentation(depth) + "converted reference to "
+ SchemaUtils.describeComponent(definition.getSchemaComponent()) + " to inline group");
}
}
}
// handle actual item count, and inlining of child group (may be new, from converted reference)
if (item instanceof GroupItem) {
// check count for nested group
GroupItem grpitem = (GroupItem)item;
int grpcount = computeComplexity(grpitem, depth + 1);
// avoid inlining if an enumeration, or an extension reference; or the nested group is optional or a
// collection and has more than one item (or a single item which is itself optional or a collection,
// which will be counted as multiple items); or the main group is a choice, and the nested group is a
// compositor with more than one element, and the first element is optional (or the first element child
// of that element, if inlined)
boolean inline = true;
if (grpitem.isEnumeration() || grpitem.isExtensionReference()) {
inline = false;
} else if (grpitem.isCollection() || grpitem.isOptional()) {
if (grpcount > 1 || ClassHolder.FORCE_COLLECTION_WRAPPER) {
inline = false;
} else {
// must be single child, but block inlining if that child is a collection
Item child;
GroupItem childgrp = grpitem;
while ((child = childgrp.getFirstChild()) instanceof GroupItem) {
childgrp = (GroupItem)child;
if (childgrp.isCollection()) {
inline = false;
break;
}
}
}
} else if (grpcount > 1 && group.getSchemaComponent().type() == SchemaBase.CHOICE_TYPE) {
// assume no inlining, but dig into structure to make sure first non-inlined element is required
inline = false;
Item child = grpitem.getFirstChild();
while (!child.isOptional()) {
if (child.getSchemaComponent().type() == SchemaBase.ELEMENT_TYPE &&
!(child instanceof GroupItem && ((GroupItem)child).isInline())) {
// required element with simple value or separate class, safe to inline
inline = true;
break;
} else if (child instanceof GroupItem) {
child = ((GroupItem)child).getFirstChild();
} else {
// required reference item, safe to inline
inline = true;
break;
}
}
}
if (inline) {
// inline the group
grpitem.setInline(true);
count += grpcount;
if (s_logger.isDebugEnabled()) {
s_logger.debug(SchemaUtils.getIndentation(depth) + "inlining "
+ (grpitem instanceof DefinitionItem ? "definition " : "group ")
+ SchemaUtils.describeComponent(grpitem.getSchemaComponent()) + " with item count " +
count);
}
} else {
// force separate class for group
grpitem.setInline(false);
count++;
}
} else {
count++;
}
// bump up the complexity if the item is optional or repeated (optionally inlining collection wrappers)
if (item.isOptional() || (ClassHolder.FORCE_COLLECTION_WRAPPER && item.isCollection())) {
count++;
}
}
return count > 1 ? 2 : count;
}
/**
* Check if a group consists only of a single non-repeating item, which is not an enumeration.
*
* @param group
* @return true
if simple group, false
if repeated, multiple, or enumeration
*/
private boolean isSimple(GroupItem group) {
if (group.isEnumeration() || group.getChildCount() > 1) {
return false;
} else {
for (Item item = group.getFirstChild(); item != null; item = item.getNext()) {
if (item.isCollection()) {
return false;
} else if (item instanceof GroupItem && !isSimple((GroupItem)item)) {
return false;
}
}
return true;
}
}
/**
* Check if a global definition structure is to be inlined. This method is mutually recursive with {@link
* #computeComplexity(GroupItem, int)}. The two methods together determine the inlining status of all items.
*
* @param definition
* @param depth nesting depth
*/
private void checkInline(DefinitionItem definition, int depth) {
if (!definition.isChecked()) {
// initially say it won't be inlined, to avoid issues with circular references
boolean forceinline = definition.isInline();
definition.setInline(false);
definition.setChecked(true);
if (s_logger.isDebugEnabled()) {
s_logger.debug(SchemaUtils.getIndentation(depth) + "checking inlining of definition "
+ SchemaUtils.describeComponent(definition.getSchemaComponent())
+ (definition.isInlineBlocked() ? " (inlining blocked)" : ""));
}
// inline references where appropriate, and count the values defined
int count = computeComplexity(definition, depth);
if (!definition.isInlineBlocked() &&
(forceinline || (count == 1 && isSimple(definition)) || definition.getReferenceCount() == 1)) {
// set state as inlined
definition.setInline(true);
if (s_logger.isDebugEnabled()) {
s_logger.debug(SchemaUtils.getIndentation(depth) + "inlining definition "
+ SchemaUtils.describeComponent(definition.getSchemaComponent()) +
" with item count " + count);
}
// convert non-inlined child components to definitions if multiple use
if (definition.getReferenceCount() > 1) {
convertToDefinitions(definition);
}
}
}
}
/**
* Convert nested groups which are not inlined to freestanding definitions. This calls itself recursively to process
* nested groups, except those nested within groups converted to definitions.
*
* @param group
*/
private void convertToDefinitions(GroupItem group) {
Item item = group.getFirstChild();
while (item != null) {
if (item instanceof GroupItem) {
GroupItem childgrp = (GroupItem)item;
if (childgrp.isInline()) {
convertToDefinitions(childgrp);
} else {
DefinitionItem definition = childgrp.convertToDefinition();
definition.setChecked(true);
OpenAttrBase ancestor = group.getSchemaComponent();
while (!ancestor.isGlobal()) {
ancestor = ancestor.getParent();
}
GlobalExtension global = (GlobalExtension)ancestor.getExtension();
PackageHolder pack = global.getPackage();
String clasname = definition.getClassName();
NameConverter nconv = global.getNameConverter();
boolean useinner = global.isUseInnerClasses();
ClassHolder clas = pack.addClass(clasname, nconv, useinner, childgrp.isEnumeration());
definition.setGenerateClass(clas);
m_definitions.add(definition);
}
}
item = item.getNext();
}
}
/**
* Generate the data model. This first builds a representation of all the data items from the schema definitions,
* then determines which items can be inlined and which need separate class representations.
*
* @throws IOException
* @throws JiBXException
*/
public void generate() throws JiBXException, IOException {
// build the item structure for each definition
ItemVisitor visitor = new ItemVisitor();
int index = 0;
ArrayList items = new ArrayList();
ArrayList checkitems = new ArrayList();
for (Iterator iter = m_validationContext.iterateSchemas(); iter.hasNext();) {
SchemaElement schema = (SchemaElement)iter.next();
m_validationContext.enterSchema(schema);
s_logger.info("Building item structure for schema " + ++index + ": " + schema.getResolver().getName());
int count = schema.getChildCount();
for (int i = 0; i < count; i++) {
SchemaBase child = schema.getChild(i);
if (child.getExtension() instanceof GlobalExtension) {
// create the definition
GlobalExtension global = (GlobalExtension)child.getExtension();
DefinitionItem definition = global.getDefinition();
if (definition == null) {
definition = visitor.buildGlobal((AnnotatedBase)child);
if (s_logger.isInfoEnabled()) {
s_logger.info("Constructed item structure for " + SchemaUtils.describeComponent(child)
+ ":\n" + definition.describe(0));
}
} else if (s_logger.isInfoEnabled()) {
s_logger.info("Found existing item structure for " + SchemaUtils.describeComponent(child)
+ ":\n" + definition.describe(0));
}
items.add(definition);
// set the names on the definition so they'll be available for inlining
NameConverter nconv = global.getNameConverter();
String dfltname = nconv.toBaseName(nconv.trimXName(((INamed)child).getName()));
String name = global.getBaseName();
if (name == null) {
name = NameConverter.toNameLead(dfltname);
}
definition.setName(name);
name = global.getClassName();
if (name == null) {
name = NameConverter.toNameWord(dfltname);
}
definition.setClassName(name);
// force class generation if required
if (global.isIncluded()) {
definition.setInlineBlocked(true);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Forcing class generation for " + SchemaUtils.describeComponent(child));
}
}
// record all simple element definition items separately for next pass
if (child.type() == SchemaBase.ELEMENT_TYPE && definition.getChildCount() == 1) {
Item item = definition.getFirstChild();
if (item instanceof ReferenceItem) {
// skip elements based on simpleTypes for this check, since simpleTypes never need a class
AnnotatedBase comp = ((ReferenceItem)item).getDefinition().getSchemaComponent();
if (comp.type() != SchemaBase.SIMPLETYPE_TYPE) {
checkitems.add(definition);
}
}
}
}
}
}
// check for special (but common) case of single global element using global complexType
Map usemap = new HashMap();
for (int i = 0; i < checkitems.size(); i++) {
ReferenceItem reference = (ReferenceItem)((DefinitionItem)checkitems.get(i)).getFirstChild();
DefinitionItem basedef = reference.getDefinition();
Boolean usedirect = (Boolean)usemap.get(basedef);
if (usedirect == null) {
// first time type was referenced, flag to use directly
usemap.put(basedef, Boolean.TRUE);
} else if (usedirect.booleanValue()) {
// second time type was referenced, flag to use separate classes
usemap.put(basedef, Boolean.FALSE);
}
}
HashMap typeinstmap = new HashMap();
for (int i = 0; i < checkitems.size(); i++) {
DefinitionItem definition = (DefinitionItem)checkitems.get(i);
ReferenceItem reference = (ReferenceItem)definition.getFirstChild();
DefinitionItem basedef = reference.getDefinition();
if (((Boolean)usemap.get(basedef)).booleanValue()) {
// single element definition using type, force class for type but none for element
basedef.setInlineBlocked(true);
definition.setInlineBlocked(false);
definition.setInline(true);
typeinstmap.put(basedef, definition);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Forcing inlining of type-isomorphic "
+ SchemaUtils.describeComponent(definition.getSchemaComponent()));
}
} else {
// multiple element definitions using type, force separate class for each
// definition.setInlineBlocked(true);
// if (s_logger.isDebugEnabled()) {
// s_logger.debug("Forcing class generation for type "
// + SchemaUtils.describeComponent(basedef.getSchemaComponent())
// + " used by multiple global elements");
// }
}
}
// compact and assign class and property names for all items
for (int i = 0; i < items.size(); i++) {
DefinitionItem definition = (DefinitionItem)items.get(i);
compactGroups(definition);
assignNames(definition, true);
if (s_logger.isInfoEnabled()) {
s_logger.info("After assigning names for "
+ SchemaUtils.describeComponent(definition.getSchemaComponent()) + ":\n" + definition.describe(0));
}
}
// inline references where appropriate
m_definitions = new ArrayList();
for (int i = 0; i < items.size(); i++) {
// make sure definition has been checked for inlining (may add new definitions directly to list)
DefinitionItem definition = (DefinitionItem)items.get(i);
checkInline(definition, 1);
if (s_logger.isInfoEnabled()) {
s_logger.info("After inlining for " + SchemaUtils.describeComponent(definition.getSchemaComponent())
+ ":\n" + definition.describe(0));
}
// add definition to list if not inlined
if (!definition.isInline()) {
m_definitions.add(definition);
GlobalExtension global = (GlobalExtension)definition.getComponentExtension();
PackageHolder pack = global.getPackage();
String cname = definition.getClassName();
NameConverter nconv = global.getNameConverter();
boolean userinner = global.isUseInnerClasses();
ClassHolder clas = pack.addClass(cname, nconv, userinner, definition.isEnumeration());
definition.setGenerateClass(clas);
}
}
// convert extension references for all global type definitions
for (int i = 0; i < m_definitions.size(); i++) {
((DefinitionItem)m_definitions.get(i)).convertExtensionReference();
}
// classify all the items by form of content
for (int i = 0; i < items.size(); i++) {
((DefinitionItem)items.get(i)).setChecked(false);
}
for (int i = 0; i < items.size(); i++) {
((DefinitionItem)items.get(i)).classifyContent();
}
// build the actual class and binding structure
m_bindingDirectory = new BindingDirectory(false, false, false, true, true);
for (int i = 0; i < m_definitions.size(); i++) {
// compact again after inlining and converting extension references
DefinitionItem definition = (DefinitionItem)m_definitions.get(i);
compactGroups(definition);
// build the binding component for this definition
ClassHolder clas = definition.getGenerateClass();
OpenAttrBase comp = definition.getSchemaComponent();
ElementBase binding;
String uri = null;
if (definition.isEnumeration()) {
FormatElement format = new FormatElement();
format.setTypeName(clas.getFullName());
((EnumerationClassHolder)clas).setBinding(format);
binding = format;
} else {
MappingElement mapping = new MappingElement();
mapping.setClassName(clas.getBindingName());
if (comp.type() == SchemaBase.ELEMENT_TYPE) {
ElementElement element = ((ElementElement)comp);
mapping.setName(element.getName());
uri = element.getQName().getUri();
} else {
mapping.setAbstract(true);
mapping.setTypeQName(((INamed)comp).getQName());
}
((StructureClassHolder)clas).setBinding(mapping);
binding = mapping;
}
if (uri == null) {
while (!(comp instanceof INamed) || ((INamed)comp).getName() == null) {
comp = comp.getParent();
}
uri = ((INamed)comp).getQName().getUri();
}
// add the mapping to appropriate binding
BindingHolder holder = m_bindingDirectory.findBinding(uri);
if (binding instanceof FormatElement) {
holder.addFormat((FormatElement)binding);
} else {
MappingElement mapping = (MappingElement)binding;
holder.addMapping(mapping);
DefinitionItem elementdef = (DefinitionItem)typeinstmap.get(definition);
if (elementdef != null) {
// add concrete mapping for element name linked to type
ElementElement element = (ElementElement)elementdef.getSchemaComponent();
MappingElement concrete = new MappingElement();
concrete.setClassName(clas.getBindingName());
concrete.setName(element.getName());
StructureElement struct = new StructureElement();
struct.setMapAsQName(mapping.getTypeQName());
concrete.addChild(struct);
holder.addMapping(concrete);
}
}
// set the definition for the class (and create any required secondary classes)
clas.createStructure(definition);
}
// build the actual classes
AST ast = AST.newAST(AST.JLS3);
ArrayList packs = m_packageDirectory.getPackages();
PackageHolder rootpack = null;
for (int i = 0; i < packs.size(); i++) {
PackageHolder pack = ((PackageHolder)packs.get(i));
if (pack.getClassCount() > 0) {
if (rootpack == null) {
PackageHolder scan = pack;
while (scan != null) {
if (scan.getClassCount() > 0 || scan.getSubpackageCount() > 1) {
rootpack = scan;
}
scan = scan.getParent();
}
}
pack.generate(ast, m_bindingDirectory);
}
}
// write the binding definition(s)
if (rootpack == null) {
rootpack = m_packageDirectory.getPackage("");
}
m_bindingDirectory.configureFiles("binding.xml", new String[0], rootpack.getName());
m_bindingDirectory.finish();
m_bindingDirectory.writeBindings(m_targetDir);
}
/**
* Report problems using console output. This clears the problem list after they've been reported, to avoid multiple
* reports of the same problems.
*
* @param vctx
* @return true
if one or more errors, false
if not
*/
private static boolean reportProblems(ValidationContext vctx) {
ArrayList probs = vctx.getProblems();
boolean error = false;
if (probs.size() > 0) {
for (int j = 0; j < probs.size(); j++) {
ValidationProblem prob = (ValidationProblem)probs.get(j);
String text;
if (prob.getSeverity() >= ValidationProblem.ERROR_LEVEL) {
error = true;
text = "Error: " + prob.getDescription();
s_logger.error(text);
} else {
text = "Warning: " + prob.getDescription();
s_logger.info(text);
}
System.out.println(text);
}
}
probs.clear();
return error;
}
/**
* Add the schemas specified by customizations to the set to be loaded.
*
* @param base root URL for schemas
* @param dir root directory for schemas
* @param custom schema set customization
* @param fileset set of schema files to be loaded
* @throws MalformedURLException
*/
private static void addCustomizedSchemas(URL base, File dir, SchemasetCustom custom, InsertionOrderedSet fileset)
throws MalformedURLException {
// first check for name match patterns supplied
String[] names = custom.getNames();
if (names != null) {
for (int i = 0; i < names.length; i++) {
SchemaNameFilter filter = new SchemaNameFilter();
String name = names[i];
filter.setPattern(name);
s_logger.debug("Matching file names to schemaset pattern '" + name + '\'');
String[] matches = dir.list(filter);
for (int j = 0; j < matches.length; j++) {
String match = matches[j];
fileset.add(new UrlResolver(match, new URL(base, match)));
s_logger.debug("Added schema from schemaset pattern match: " + match);
}
}
}
// next check all child customizations
LazyList childs = custom.getChildren();
for (int i = 0; i < childs.size(); i++) {
Object child = childs.get(i);
if (child instanceof SchemaCustom) {
String name = ((SchemaCustom)child).getName();
if (name != null) {
try {
fileset.add(new UrlResolver(name, new URL(base, name)));
s_logger.debug("Added schema from customizations: " + name);
} catch (MalformedURLException e) {
System.out.println("Error adding schema from customizations: " + name);
}
}
} else if (child instanceof SchemasetCustom) {
addCustomizedSchemas(base, dir, (SchemasetCustom)child, fileset);
}
}
}
/**
* Get the package directory used for code generation.
*
* @return directory
*/
public PackageDirectory getPackageDirectory() {
return m_packageDirectory;
}
/**
* Run the binding generation using command line parameters.
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
TreeWalker.setLogging(Level.ERROR);
CodeGeneratorCommandLine parms = new CodeGeneratorCommandLine();
if (args.length > 0 && parms.processArgs(args)) {
// build set of schemas specified on command line (including via wildcards)
InsertionOrderedSet fileset = new InsertionOrderedSet();
URL base = parms.getSchemaRoot();
File basedir = parms.getSchemaDir();
SchemaNameFilter filter = new SchemaNameFilter();
boolean err = false;
for (Iterator iter = parms.getExtraArgs().iterator(); iter.hasNext();) {
String name = (String)iter.next();
if (name.indexOf('*') >= 0) {
if (basedir == null) {
System.err.println("File name pattern argument not allowed for non-file base: '" + name + '\'');
} else {
filter.setPattern(name);
s_logger.debug("Matching file names to command line pattern '" + name + '\'');
String[] matches = basedir.list(filter);
for (int i = 0; i < matches.length; i++) {
String match = matches[i];
fileset.add(new UrlResolver(match, new URL(base, match)));
}
}
} else {
if (basedir == null) {
URL url = new URL(base, name);
s_logger.debug("Adding schema URL from command line: " + url.toExternalForm());
fileset.add(new UrlResolver(name, url));
} else {
File sfile = new File(basedir, name);
if (sfile.exists()) {
s_logger.debug("Adding schema file from command line: " + sfile.getCanonicalPath());
fileset.add(new UrlResolver(name, new URL(base, name)));
} else {
System.err.println("Schema file from command line not found: " + sfile.getAbsolutePath());
err = true;
}
}
}
}
if (!err) {
// add any schemas specified in customizations
addCustomizedSchemas(base, basedir, parms.getCustomRoot(), fileset);
// load the full set of schemas
CodeGenerator inst = new CodeGenerator(parms);
SchemaElement[] schemas = inst.load(fileset.asList());
if (!reportProblems(inst.m_validationContext)) {
if (inst.customizeSchemas()) {
System.out.println("Loaded and validated " + fileset.size() + " schemas");
// apply the customizations to the schema
inst.applyAndNormalize();
inst.pruneDefinitions();
// revalidate and run the generation
inst.validateSchemas(schemas);
inst.generate();
// check if dump file to be output
File dump = parms.getDumpFile();
if (dump != null) {
// delete the file it it already exists
if (dump.exists()) {
dump.delete();
}
dump.createNewFile();
// now print out the class details by package
BufferedWriter writer = new BufferedWriter(new FileWriter(dump));
DataModelUtils.writeImage(inst.m_packageDirectory, writer);
writer.close();
}
}
}
}
} else {
if (args.length > 0) {
System.err.println("Terminating due to command line errors");
} else {
parms.printUsage();
}
System.exit(1);
}
}
/**
* File name pattern matcher.
*/
private static class SchemaNameFilter implements FilenameFilter
{
/** Current match pattern. */
private String m_pattern;
/**
* Set the match pattern.
*
* @param pattern
*/
public void setPattern(String pattern) {
m_pattern = pattern;
}
/**
* Check for file name match.
*
* @param dir
* @param name
* @return match flag
*/
public boolean accept(File dir, String name) {
boolean match = SchemasetCustom.isPatternMatch(name, m_pattern);
if (match) {
s_logger.debug(" matched file name '" + name + '\'');
}
return match;
}
}
}