001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.contentio.export.sql; 017 018import java.sql.Connection; 019import java.util.HashMap; 020import java.util.Map; 021 022import org.apache.avalon.framework.component.Component; 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 025import org.apache.avalon.framework.context.Contextualizable; 026import org.apache.cocoon.components.ContextHelper; 027import org.apache.cocoon.environment.Request; 028import org.apache.commons.lang.StringUtils; 029import org.apache.commons.lang.WordUtils; 030 031import org.ametys.core.datasource.ConnectionHelper; 032import org.ametys.runtime.plugin.component.AbstractLogEnabled; 033 034/** 035 * Normalize name component 036 */ 037public class NormalizeNameComponent extends AbstractLogEnabled implements Component, Contextualizable 038{ 039 /** The component role */ 040 public static final String ROLE = NormalizeNameComponent.class.getName(); 041 042 /** The avalon context. */ 043 protected Context _context; 044 045 public void contextualize(Context context) throws ContextException 046 { 047 _context = context; 048 } 049 050 /** 051 * Get the normalized table name 052 * @param prefix the table name prefix 053 * @param mappingPolicy the mapping policy 054 * @param initialTableName The initial table name 055 * @param connection the connection 056 * @return normalized table name 057 */ 058 protected String normalizedTableName (String prefix, String mappingPolicy, String initialTableName, Connection connection) 059 { 060 Map<String, String> mappingTableName = getMappingTableNameFromCache(); 061 if (mappingTableName.containsKey(initialTableName)) 062 { 063 return mappingTableName.get(initialTableName); 064 } 065 else 066 { 067 String initialTableNameNoPrefix = initialTableName.substring(prefix.length()); 068 String normalizedTableName = initialTableNameNoPrefix.replace("-", "_"); 069 StringBuilder changedInitialName = new StringBuilder(); 070 int i = 0; 071 072 for (String name : normalizedTableName.split("_")) 073 { 074 if (i != 0) 075 { 076 changedInitialName.append("_"); 077 } 078 changedInitialName.append(normalizeNameFromPolicy(mappingPolicy, StringUtils.capitalize(name))); 079 i++; 080 } 081 082 normalizedTableName = prefix + changedInitialName.toString(); 083 084 int maxLength = Integer.MAX_VALUE; 085 086 String datatype = ConnectionHelper.getDatabaseType(connection); 087 if (datatype.equals(ConnectionHelper.DATABASE_MYSQL)) 088 { 089 maxLength = 64 /* mysql max */; 090 } 091 else if (datatype.equals(ConnectionHelper.DATABASE_ORACLE)) 092 { 093 maxLength = 30 /* oracle max */; 094 } 095 096 if (normalizedTableName.length() >= maxLength) 097 { 098 final int hashSize = 6; 099 normalizedTableName = normalizedTableName.substring(0, maxLength - hashSize) + String.valueOf((int) Math.abs(normalizedTableName.hashCode() % Math.pow(10, hashSize))); 100 } 101 102 if (mappingTableName.containsValue(normalizedTableName)) 103 { 104 normalizedTableName = StringUtils.substring(normalizedTableName, 0, normalizedTableName.length() - 2) + "_" + (_getCountSameValue(normalizedTableName, mappingTableName, 1)); 105 } 106 107 mappingTableName.put(initialTableName, normalizedTableName); 108 109 return normalizedTableName; 110 } 111 } 112 113 /** 114 * Get the normalized column name 115 * @param mappingPolicy the mappingPolicy 116 * @param initialColumnName The initial column name 117 * @param initialTableName The initial table name 118 * @param reservedWords the map of reserved words 119 * @param connection the connection 120 * @return normalized column name 121 */ 122 protected String normalizedColumnName (String mappingPolicy, String initialColumnName, String initialTableName, Map<String, Map<String, String>> reservedWords, Connection connection) 123 { 124 String datatype = ConnectionHelper.getDatabaseType(connection); 125 String columnName = initialColumnName; 126 if (datatype.equals(ConnectionHelper.DATABASE_MYSQL)) 127 { 128 Map<String, String> mysqlReservedWords = reservedWords.get("mysql"); 129 if (mysqlReservedWords.containsKey(StringUtils.upperCase(initialColumnName))) 130 { 131 columnName = StringUtils.lowerCase(mysqlReservedWords.get(StringUtils.upperCase(initialColumnName))); 132 } 133 } 134 else if (datatype.equals(ConnectionHelper.DATABASE_ORACLE)) 135 { 136 Map<String, String> oracleReservedWords = reservedWords.get("oracle"); 137 if (oracleReservedWords.containsKey(StringUtils.upperCase(initialColumnName))) 138 { 139 columnName = StringUtils.lowerCase(oracleReservedWords.get(StringUtils.upperCase(initialColumnName))); 140 } 141 } 142 143 Map<String, HashMap<String, String>> mappingTableColumnName = getMappingTableColumnNameFromCache(); 144 HashMap<String, String> mappingColumnName = new HashMap<>(); 145 if (!mappingTableColumnName.containsKey(initialTableName)) 146 { 147 mappingTableColumnName.put(initialTableName, mappingColumnName); 148 } 149 150 mappingColumnName = mappingTableColumnName.get(initialTableName); 151 if (mappingColumnName.containsKey(columnName)) 152 { 153 return mappingColumnName.get(columnName); 154 } 155 else 156 { 157 String normalizedColumnName = columnName.replace("-", "_"); 158 StringBuilder changedInitialName = new StringBuilder(); 159 int i = 0; 160 for (String name : normalizedColumnName.split("_")) 161 { 162 if (i != 0) 163 { 164 changedInitialName.append("_"); 165 } 166 167 changedInitialName.append(normalizeNameFromPolicy(mappingPolicy, StringUtils.capitalize(name))); 168 i++; 169 } 170 171 normalizedColumnName = changedInitialName.toString(); 172 173 int maxLength = Integer.MAX_VALUE; 174 175 if (datatype.equals(ConnectionHelper.DATABASE_MYSQL)) 176 { 177 maxLength = 64 /* mysql max */; 178 } 179 else if (datatype.equals(ConnectionHelper.DATABASE_ORACLE)) 180 { 181 maxLength = 30 /* oracle max */; 182 } 183 184 if (normalizedColumnName.length() > maxLength) 185 { 186 final int hashSize = 9; 187 normalizedColumnName = normalizedColumnName.substring(0, maxLength - hashSize) + String.valueOf((int) Math.abs(normalizedColumnName.hashCode() % Math.pow(10, hashSize))); 188 } 189 190 if (mappingColumnName.containsValue(normalizedColumnName)) 191 { 192 normalizedColumnName = StringUtils.substring(normalizedColumnName, 0, normalizedColumnName.length() - 2) + "_" + (_getCountSameValue(normalizedColumnName, mappingColumnName, 1)); 193 } 194 195 mappingColumnName.put(columnName, normalizedColumnName); 196 197 return normalizedColumnName; 198 } 199 } 200 201 private int _getCountSameValue(String value, Map<String, String> mappingTable, int count) 202 { 203 String valueAux = value; 204 int countAux = count; 205 for (String val : mappingTable.values()) 206 { 207 if (val.equals(valueAux)) 208 { 209 countAux++; 210 valueAux = StringUtils.substring(valueAux, 0, valueAux.length() - 2) + "_" + countAux; 211 return _getCountSameValue(valueAux, mappingTable, countAux); 212 } 213 } 214 215 return countAux; 216 } 217 218 /** 219 * Get the normalized comment 220 * @param initialComment the initial comment 221 * @param maxLength The maximun number of authorized characters 222 * @param connection the connection 223 * @return normalized comment 224 */ 225 protected String normalizedComment (String initialComment, int maxLength, Connection connection) 226 { 227 String normalizedComment = escapeValue(initialComment, connection); 228 229 if (maxLength != 0 && normalizedComment.length() > maxLength) 230 { 231 normalizedComment = normalizedComment.substring(0, maxLength - 3) + "..."; 232 } 233 234 return normalizedComment; 235 } 236 237 /** 238 * Escape column value or comment 239 * @param value The value or SQL comment 240 * @param connection the connection 241 * @return the escaped value 242 */ 243 protected String escapeValue (String value, Connection connection) 244 { 245 String newValue = value; 246 String datatype = ConnectionHelper.getDatabaseType(connection); 247 if (datatype.equals(ConnectionHelper.DATABASE_MYSQL)) 248 { 249 newValue = value.replace("\'", "\\'"); 250 } 251 else if (datatype.equals(ConnectionHelper.DATABASE_ORACLE)) 252 { 253 newValue = value.replace("'", "''"); 254 } 255 256 return newValue; 257 } 258 259 /** 260 * Get the table name from the cache 261 * @return the mapping for table name 262 */ 263 @SuppressWarnings("unchecked") 264 protected Map<String, String> getMappingTableNameFromCache() 265 { 266 Request request = ContextHelper.getRequest(_context); 267 268 Map<String, String> mappingTableName = new HashMap<>(); 269 if (request.getAttribute("mappingTableName") != null) 270 { 271 mappingTableName = (Map<String, String>) request.getAttribute("mappingTableName"); 272 } 273 else 274 { 275 request.setAttribute("mappingTableName", mappingTableName); 276 } 277 278 return mappingTableName; 279 } 280 281 /** 282 * Get the column name from the cache 283 * @return the mapping for column name 284 */ 285 @SuppressWarnings("unchecked") 286 protected Map<String, HashMap<String, String>> getMappingTableColumnNameFromCache() 287 { 288 Request request = ContextHelper.getRequest(_context); 289 290 Map<String, HashMap<String, String>> mappingTableColumnName = new HashMap<>(); 291 if (request.getAttribute("mappingTableColumnName") != null) 292 { 293 mappingTableColumnName = (Map<String, HashMap<String, String>>) request.getAttribute("mappingTableColumnName"); 294 } 295 else 296 { 297 request.setAttribute("mappingTableColumnName", mappingTableColumnName); 298 } 299 300 return mappingTableColumnName; 301 } 302 303 /** 304 * Normalize the initialName into a name depends on the mapping Policy 305 * @param mappingPolicy (FULL, CAMELCASE, FIRSTCHAR) 306 * @param initialName the initial table name 307 * @return the name changed depends on the mapping Policy 308 */ 309 protected String normalizeNameFromPolicy(String mappingPolicy, String initialName) 310 { 311 String modifiedName = ""; 312 if (mappingPolicy.equals("FULL")) 313 { 314 modifiedName = initialName; 315 } 316 else if (mappingPolicy.equals("CAMELCASE")) 317 { 318 modifiedName = initialName.replaceAll("(\\p{Ll})(\\p{Lu})", "$1 $2"); 319 modifiedName = WordUtils.initials(modifiedName); 320 } 321 else if (mappingPolicy.equals("FIRSTCHAR")) 322 { 323 modifiedName = initialName.replaceAll("(\\p{Ll})(\\p{Lu})", "$1 $2"); 324 String[] words = modifiedName.split(" "); 325 326 StringBuilder finalName = new StringBuilder(); 327 for (String word : words) 328 { 329 if (word.length() > 3) 330 { 331 for (int i = 3; i < word.length(); i++) 332 { 333 char charWord = word.charAt(i); 334 if (Character.isUpperCase(charWord) 335 || charWord == 'a' || charWord == 'e' || charWord == 'i' 336 || charWord == 'o' || charWord == 'u' || charWord == 'y') 337 { 338 finalName.append(word.substring(0, i)); 339 break; 340 } 341 else if (i == word.length() - 1) 342 { 343 finalName.append(word); 344 } 345 } 346 } 347 else 348 { 349 finalName.append(word); 350 } 351 } 352 modifiedName = finalName.toString(); 353 } 354 355 return modifiedName; 356 } 357}