001package com.avaje.ebean.config;
002
003import com.avaje.ebean.util.CamelCaseHelper;
004
005/**
006 * Converts between Camel Case and Underscore based names for both table and
007 * column names (and is the default naming convention in Ebean).
008 *
009 * @author emcgreal
010 * @author rbygrave
011 */
012public class UnderscoreNamingConvention extends AbstractNamingConvention {
013
014  /**
015   * Force toUnderscore to return in upper case.
016   */
017  private boolean forceUpperCase = false;
018
019  /**
020   * The digits compressed.
021   */
022  private boolean digitsCompressed = true;
023
024  /**
025   * Create with a given sequence format.
026   *
027   * @param sequenceFormat the sequence format
028   */
029  public UnderscoreNamingConvention(String sequenceFormat) {
030    super(sequenceFormat);
031  }
032
033  /**
034   * Create with a sequence format of "{table}_seq".
035   */
036  public UnderscoreNamingConvention() {
037    super();
038  }
039
040  /**
041   * Returns the last part of the class name.
042   *
043   * @param beanClass the bean class
044   * @return the table name from class
045   */
046  public TableName getTableNameByConvention(Class<?> beanClass) {
047
048    return new TableName(getCatalog(), getSchema(), toUnderscoreFromCamel(beanClass.getSimpleName()));
049  }
050
051  /**
052   * Converts Camel case property name to underscore based column name.
053   *
054   * @return the column from property
055   */
056  public String getColumnFromProperty(Class<?> beanClass, String propertyName) {
057
058    return toUnderscoreFromCamel(propertyName);
059  }
060
061  /**
062   * Converts underscore based column name to Camel case property name.
063   *
064   * @param beanClass    the bean class
065   * @param dbColumnName the db column name
066   * @return the property from column
067   */
068  public String getPropertyFromColumn(Class<?> beanClass, String dbColumnName) {
069    return toCamelFromUnderscore(dbColumnName);
070  }
071
072  /**
073   * Return true if the result will be upper case.
074   * <p>
075   * False if it will be lower case.
076   * </p>
077   */
078  public boolean isForceUpperCase() {
079    return forceUpperCase;
080  }
081
082  /**
083   * Set to true to make the result upper case.
084   */
085  public void setForceUpperCase(boolean forceUpperCase) {
086    this.forceUpperCase = forceUpperCase;
087  }
088
089  /**
090   * Returns true if digits are compressed.
091   */
092  public boolean isDigitsCompressed() {
093    return digitsCompressed;
094  }
095
096  /**
097   * Sets to true for digits to be compressed (without a leading underscore).
098   */
099  public void setDigitsCompressed(boolean digitsCompressed) {
100    this.digitsCompressed = digitsCompressed;
101  }
102
103  /**
104   * Convert and return the string to underscore from camel case.
105   */
106  protected String toUnderscoreFromCamel(String camelCase) {
107
108    int lastUpper = -1;
109    StringBuilder sb = new StringBuilder(camelCase.length()+4);
110    for (int i = 0; i < camelCase.length(); i++) {
111      char c = camelCase.charAt(i);
112
113      if ('_' == c) {
114        // Underscores should just be passed through
115        sb.append(c);
116        lastUpper = i;
117      } else if (Character.isDigit(c)) {
118        if (i > lastUpper + 1 && !digitsCompressed) {
119          sb.append("_");
120        }
121        sb.append(c);
122        lastUpper = i;
123
124      } else if (Character.isUpperCase(c)) {
125        if (i > lastUpper + 1) {
126          sb.append("_");
127        }
128        sb.append(Character.toLowerCase(c));
129        lastUpper = i;
130
131      } else {
132        sb.append(c);
133      }
134    }
135    String ret = sb.toString();
136    if (forceUpperCase) {
137      ret = ret.toUpperCase();
138    }
139    return ret;
140  }
141
142  /**
143   * Convert and return the from string from underscore to camel case.
144   */
145  protected String toCamelFromUnderscore(String underscore) {
146    return CamelCaseHelper.toCamelFromUnderscore(underscore);
147  }
148}