blob: 1f3326d8bb31f7fc62b58ca909b5d6cb9ca401a0 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.binding.compile;
import org.apache.xmlbeans.impl.jam.JClass;
import org.apache.xmlbeans.impl.jam.JProperty;
import org.apache.xmlbeans.impl.common.NameUtil;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaProperty;
import org.apache.xmlbeans.SchemaTypeSystem;
import javax.xml.namespace.QName;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
/**
* This is a simple implementation of a type matcher
* that uses the names of the Java classes and Schema
* types, and Java properties and Schema elements or
* attributes, to line them up with each other.
*/
public class DefaultTypeMatcher implements TypeMatcher
{
private TypeMatcherContext mContext;
public void init(TypeMatcherContext ctx) {
mContext = ctx;
}
public MatchedType[] matchTypes(JClass[] jClasses, SchemaTypeSystem sts) {
SchemaType[] types = sts.globalTypes();
List result = new ArrayList();
// start the match
startMatch();
// add all the jClasses
// todo: there is a need for some code which does
// "primary interfaces" rather than "impl classes". This
// should be plugin behavior or controlled by annotations
// rather than hard-coded.
for (int i = 0; i < jClasses.length; i++) {
putJavaName(jClasses[i].getSimpleName(), jClasses[i]);
}
// Then, go through all the Schema types with complex content
// and try to find a matching Java class by looking for a similar
// name.
for (int i = 0; i < types.length; i++) {
JClass jClass = (JClass)getSchemaName(types[i].getName());
if (jClass != null)
result.add(new MatchedType(jClass, types[i]));
}
return (MatchedType[])result.toArray(new MatchedType[result.size()]);
}
public TypeMatcher.MatchedProperties[] matchProperties(JClass jClass, SchemaType sType)
{
SchemaProperty[] sProps = sType.getProperties();
JProperty[] jProps = jClass.getProperties();
List result = new ArrayList();
// start the match
startMatch();
// add all the jProperties
for (int i = 0; i < jProps.length; i++) {
putJavaName(jProps[i].getSimpleName(), jProps[i]);
}
// Then, go through all the Schema properties and try to find a
// matching Java property by looking for a similar name.
for (int i = 0; i < sProps.length; i++) {
JProperty jProp = (JProperty)getSchemaName(sProps[i].getName());
if (jProp != null)
result.add(new MatchedProperties(jProp, sProps[i]));
}
return (MatchedProperties[])result.toArray(new MatchedProperties[result.size()]);
}
public JClass substituteClass(JClass declaredClass) {
return declaredClass;
}
public void startMatch()
{
mapByShortName.clear();
mapByLowercasedShortName.clear();
}
private final Map mapByShortName = new HashMap();
private final Map mapByLowercasedShortName = new HashMap();
/**
* Returns false if the name has already been used.
* Otherwise, indexes the given object by the given Java name.
*
* Note that there is logic to be case-insensitive, yet to
* remove case-insensitivity for names which differ from
* each other only by case.
*/
public boolean putJavaName(String key, Object value)
{
String shortName = key;
verbose("JavaNameMatcher.put " + key);
if (mapByShortName.containsKey(shortName))
return false;
mapByShortName.put(shortName, value);
String lcShortName = shortName.toLowerCase();
if (mapByLowercasedShortName.containsKey(lcShortName))
mapByLowercasedShortName.put(lcShortName, null);
else
mapByLowercasedShortName.put(lcShortName, value);
return true;
}
/**
* Attempts to find a Java name similar to the given QName.
* If found, returns the indexed object; otherwise returns
* null.
*
* Note that this algorithm searches for the localName to
* match exactly; then in a case-insensitive way; and then
* with XML punctuation removed and camel-casing applied;
* and then in a case-insensitive way again.
*/
public Object getSchemaName(QName name)
{
Object result = null;
String localName = name.getLocalPart();
verbose("JavaNameMatcher.getLocalPart " + localName);
result = mapByShortName.get(localName);
if (result != null) {
verbose("javaTypeByShortName.get(localName): "+ localName);
return result;
}
String lcLocalName = localName.toLowerCase();
verbose("JavaNameMatcher.lcLocalName " + lcLocalName);
result = mapByLowercasedShortName.get(lcLocalName);
if (result != null) {
verbose("javaTypeByLowercasedShortName.get(lcLocalName): " + lcLocalName);
return result;
}
String niceName = NameUtil.upperCamelCase(localName);
verbose("JavaNameMatcher.jaxbName " + niceName);
result = mapByShortName.get(niceName);
if (result != null) {
verbose("javaTypeByShortName.get(jaxbName): " + niceName);
return result;
}
String lowercaseNiceName = niceName.toLowerCase();
verbose("JavaNameMatcher.lcJaxbName " + lowercaseNiceName);
result = mapByLowercasedShortName.get(lowercaseNiceName);
if (result != null) {
verbose("javaTypeByShortName.get(lcJaxbName): " + lowercaseNiceName);
return result;
}
verbose("javaTypeByShortName.get() no match found: " + localName);
return null;
}
private void verbose(String w) {
mContext.getLogger().logVerbose(w);
}
}