/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cayenne.dbsync.naming;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.util.Util;
import org.jvnet.inflector.Noun;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* The default strategy for converting DB-layer to Object-layer names.
*
* @since 4.0
*/
public class DefaultObjectNameGenerator implements ObjectNameGenerator {
private DbEntityNameStemmer dbEntityNameStemmer;
public DefaultObjectNameGenerator() {
this.dbEntityNameStemmer = NoStemStemmer.getInstance();
}
public DefaultObjectNameGenerator(DbEntityNameStemmer dbEntityNameStemmer) {
this.dbEntityNameStemmer = dbEntityNameStemmer;
}
@Override
public String relationshipName(DbRelationship... relationshipChain) {
if (relationshipChain == null || relationshipChain.length < 1) {
throw new IllegalArgumentException("At least on relationship is expected: " + relationshipChain);
}
// ignore the name of DbRelationship itself (FWIW we may be generating a new name for it here)...
// generate the name based on join semantics...
String name = isToMany(relationshipChain)
? toManyRelationshipName(relationshipChain)
: toOneRelationshipName(relationshipChain);
return Util.underscoredToJava(name, false);
}
protected boolean isToMany(DbRelationship... relationshipChain) {
for (DbRelationship r : relationshipChain) {
if (r.isToMany()) {
return true;
}
}
return false;
}
protected String stemmed(String dbEntityName) {
return dbEntityNameStemmer.stem(Objects.requireNonNull(dbEntityName));
}
protected String toManyRelationshipName(DbRelationship... relationshipChain) {
DbRelationship last = relationshipChain[relationshipChain.length - 1];
String baseName = stemmed(last.getTargetEntityName());
try {
// by default we use English rules here...
return Noun.pluralOf(baseName.toLowerCase(), Locale.ENGLISH);
} catch (Exception inflectorError) {
// seems that Inflector cannot be trusted. For instance, it
// throws an exception when invoked for word "ADDRESS" (although
// lower case works fine). To feel safe, we use superclass'
// behavior if something's gone wrong
return baseName;
}
}
protected String toOneRelationshipName(DbRelationship... relationshipChain) {
DbRelationship first = relationshipChain[0];
DbRelationship last = relationshipChain[relationshipChain.length - 1];
List<DbJoin> joins = first.getJoins();
if (joins.isEmpty()) {
// In case, when uses EditRelationship button, relationship doesn't exist => it doesn't have joins
// and just return targetName
return stemmed(last.getTargetEntityName());
}
DbJoin join1 = joins.get(0);
// TODO: multi-join relationships
// return the name of the FK column sans ID
String fkColName = join1.getSourceName();
if (fkColName == null) {
return stemmed(last.getTargetEntityName());
} else if (fkColName.toUpperCase().endsWith("_ID") && fkColName.length() > 3) {
return fkColName.substring(0, fkColName.length() - 3);
} else if (fkColName.toUpperCase().endsWith("ID") && fkColName.length() > 2) {
return fkColName.substring(0, fkColName.length() - 2);
} else {
return stemmed(last.getTargetEntityName());
}
}
@Override
public String objEntityName(DbEntity dbEntity) {
String baseName = stemmed(dbEntity.getName());
return Util.underscoredToJava(baseName, true);
}
@Override
public String objAttributeName(DbAttribute attr) {
return Util.underscoredToJava(attr.getName(), false);
}
}