001 /* 002 Copyright (C) 2001-2002 Renaud Pawlak <renaud@aopsys.com> 003 Laurent Martelli <laurent@aopsys.com> 004 005 This program is free software; you can redistribute it and/or modify 006 it under the terms of the GNU Lesser General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This program is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 GNU Lesser General Public License for more details. 014 015 You should have received a copy of the GNU Lesser General Public License 016 along with this program; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 018 019 package org.objectweb.jac.core.rtti; 020 021 import java.lang.reflect.*; 022 import java.util.*; 023 024 /** 025 * This class provides some useful methods to get some information 026 * regarding the naming conventions. 027 * 028 * @author Renaud Pawlak 029 * @author Laurent Martelli 030 */ 031 032 public class NamingConventions { 033 034 /** Constant to represent field modifiers. */ 035 public static final int MODIFIER = 0; 036 037 /** Constant to represent field getters. */ 038 public static final int GETTER = 1; 039 040 /** Constant to represent field setters. */ 041 public static final int SETTER = 2; 042 043 /** Constant to represent collection adders. */ 044 public static final int ADDER = 3; 045 046 /** Constant to represent collection removers. */ 047 public static final int REMOVER = 4; 048 049 /** Store default field getter prefixes (get...). */ 050 public static final String[] getterPrefixes; 051 052 /** Store default field setter prefixes (set...). */ 053 public static final String[] setterPrefixes; 054 055 /** Store default collection adder prefixes (add..., put...). */ 056 public static final String[] adderPrefixes; 057 058 /** Store default collection remover prefixes (rmv..., del..., 059 remove...). */ 060 public static final String[] removerPrefixes; 061 062 static { 063 getterPrefixes = new String[] { "get", "is" }; 064 setterPrefixes = new String[] { "set" }; 065 adderPrefixes = new String[] { "add","put" }; 066 removerPrefixes = new String[] { "rmv", "del", "remove", "clear" }; 067 } 068 069 /** 070 * Returns the short name of a class. 071 * 072 * <p>If it is a well-known java class or a well-known JAC class, 073 * truncates the packages to give only the class name. It is is an 074 * array, add [] after the primitive type. 075 * 076 * @param cli the class item 077 */ 078 public static String getShortClassName(ClassItem cli) { 079 return getShortClassName(cli.getActualClass()); 080 } 081 082 /** 083 * Returns the short name of a class. 084 * 085 * <p>If it is a well-known java class or a well-known JAC class, 086 * truncates the packages to give only the class name. It is is an 087 * array, add [] after the primitive type. 088 * 089 * @param cl the class 090 */ 091 public static String getShortClassName(Class cl) { 092 093 String type = cl.getName(); 094 095 if (cl.isArray()) { 096 type = cl.getComponentType().getName(); 097 } 098 099 if (type.startsWith("java") || type.startsWith( "org.objectweb.jac")) { 100 type = type.substring(type.lastIndexOf( '.' )+1); 101 } 102 103 if (cl.isArray()) { 104 type = type + "[]"; 105 } 106 107 return type; 108 109 } 110 111 public static String getStandardClassName(Class cl) { 112 String type = cl.getName(); 113 if ( cl.isArray() ) { 114 type = cl.getComponentType().getName()+"[]"; 115 } 116 return type; 117 } 118 119 /** 120 * Returns the short name of a given constructor. 121 * 122 * <p>By default, the name of a constructor contains the full path 123 * name of the constructor class. Thus, this method is equivalent 124 * to get the short name of the constructor class. 125 * 126 * @param constructor a constructor 127 * @return its short name 128 * @see #getShortClassName(Class) */ 129 130 public static String getShortConstructorName(Constructor constructor) { 131 String name = constructor.getName(); 132 if ( name.lastIndexOf( '.' ) == -1 ) { 133 return name; 134 } 135 return name.substring(name.lastIndexOf( '.' ) + 1); 136 } 137 138 /** 139 * Returns the short name of a given constructor. 140 * 141 * <p>By default, the name of a constructor contains the full path 142 * name of the constructor class. Thus, this method is equivalent 143 * to get the short name of the constructor class. 144 * 145 * @param constructor a constructor 146 * @return its short name 147 * @see #getShortClassName(ClassItem) */ 148 149 public static String getShortConstructorName(ConstructorItem constructor) { 150 return getShortConstructorName(constructor.getActualConstructor()); 151 } 152 153 /** 154 * Returns the package name of the given class. 155 * 156 * @param cl the class to get the package of 157 * @return the name of the package where <code>cl</code> is defined 158 */ 159 160 public static String getPackageName(Class cl) { 161 String type = cl.getName(); 162 163 return type.substring(0,type.lastIndexOf(".")); 164 } 165 166 /** 167 * Returns a printable representation of the argument types of a 168 * method (or a constructor if needed). 169 * 170 * <p>For instance, for a method that takes one object, one string, 171 * and one string array, the result will look like "Object, String, 172 * String[]". 173 * 174 * @param method the involved method 175 * @return a printable string 176 * @see #getPrintableParameterTypes(AbstractMethodItem) */ 177 178 public static String getPrintableParameterTypes(AccessibleObject method) { 179 String ret = "("; 180 181 Class[] pts; 182 if ( method instanceof Constructor ) 183 pts = ((Constructor)method).getParameterTypes(); 184 else if ( method instanceof Method ) 185 pts = ((Method)method).getParameterTypes(); 186 else return ""; 187 for ( int j = 0; j < pts.length; j++ ) { 188 ret = ret + getShortClassName( pts[j] ); 189 if ( j < pts.length - 1 ) ret = ret + ","; 190 } 191 ret = ret + ")"; 192 return ret; 193 } 194 195 /** 196 * Returns a printable representation of the argument types of a 197 * method (or a constructor if needed). 198 * 199 * <p>Same as its homonym but uing RTTI meta item. 200 * 201 * @param method the involved method 202 * @return a printable string 203 * @see #getPrintableParameterTypes(AccessibleObject) */ 204 205 public static String getPrintableParameterTypes(AbstractMethodItem method) { 206 String ret = "("; 207 208 Class[] pts = method.getParameterTypes(); 209 for ( int j = 0; j < pts.length; j++ ) { 210 ret = ret + getShortClassName( pts[j] ); 211 if ( j < pts.length - 1 ) ret = ret + ","; 212 } 213 ret = ret + ")"; 214 return ret; 215 } 216 217 /** 218 * Returns a declared method of a class <code>c</code> only by 219 * knowing its name. Returns null if not found (to be modified to 220 * raise an exception. 221 * 222 * @param c the class. 223 * @param name the name of the method to find. 224 * @return the method (null if not found). */ 225 226 public static Method getDeclaredMethodByName(Class c, String name) { 227 228 Method[] methods = c.getDeclaredMethods(); 229 230 for ( int i=0 ; i < methods.length ; i++) { 231 if ( name.equals(methods[i].getName()) ) { 232 return methods[i]; 233 } 234 } 235 236 return null; 237 } 238 239 /** 240 * Tell if the string is prefixed with one of the given prefixes. 241 * 242 * <p>Returns 0 if the candidate is not prefixed by any of the 243 * given prefixes and also if the candidate exactly equals one of 244 * the prefixes. 245 * 246 * @param candidate the string to test 247 * @param prefixes the tested prefixes 248 * 249 * @return the length of the matching prefix, 0 if not prefixed */ 250 251 public static int isPrefixedWith(String candidate, String[] prefixes) { 252 for ( int i = 0; i < prefixes.length; i++ ) { 253 if ( prefixes[i].equals(candidate) ) 254 return 0; 255 if ( candidate.startsWith(prefixes[i]) ) 256 return prefixes[i].length(); 257 } 258 return 0; 259 } 260 261 /** 262 * Tells if the the method name is equal to one of the given 263 * prefixes. 264 * 265 * @return true if equals */ 266 267 public static boolean isInPrefixes(String candidate, String[] prefixes) { 268 for ( int i = 0; i < prefixes.length; i++ ) { 269 if ( prefixes[i].equals(candidate) ) return true; 270 } 271 return false; 272 } 273 274 /** 275 * Return true if the name matches a setter profile (i.e. set...). 276 * 277 * @param name the string to test 278 * @return true if setter profile 279 */ 280 281 public static boolean isSetter(String name) { 282 return isPrefixedWith(name, setterPrefixes) != 0 ; 283 } 284 285 /** 286 * Return true if the name matches a getter profile (i.e. get...). 287 * 288 * @param name the string to test 289 * @return true if getter profile */ 290 291 public static boolean isGetter(String name) { 292 return isPrefixedWith(name, getterPrefixes) != 0 ; 293 } 294 295 /** 296 * Return true if the name matches a adder profile (i.e. add...). 297 * 298 * @param name the string to test 299 * @return true if adder profile */ 300 301 public static boolean isAdder(String name) { 302 return isPrefixedWith(name, adderPrefixes) != 0 ; 303 } 304 305 /** 306 * Return true if the name matches a remover profile (i.e. rmv..., 307 * del..., remove...). 308 * 309 * @param name the string to test 310 * @return true if remover profile */ 311 312 public static boolean isRemover(String name) { 313 return isPrefixedWith( name, removerPrefixes ) != 0 ; 314 } 315 316 /** 317 * Returns true if the name matches a modifier profile 318 * (i.e. setter, adder, or remover profile). 319 * 320 * @param name the string to test 321 * @return true if modifier profile */ 322 323 public static boolean isModifier(String name) { 324 return isSetter(name) || isAdder(name) || isRemover(name) || 325 isInPrefixes(name, removerPrefixes) || isInPrefixes(name, adderPrefixes); 326 } 327 328 /** 329 * Takes a string and returns a new capitalized one. 330 * 331 * @param str the original string 332 * @return a new capitalized version of <code>str</code> */ 333 334 public static String capitalize(String str) { 335 if ( str.length() == 0 ) 336 return str; 337 StringBuffer sb = new StringBuffer(str); 338 sb.setCharAt(0, Character.toUpperCase(str.charAt(0)) ); 339 return sb.toString(); 340 } 341 342 /** 343 * Returns the normalized name for a string. 344 * 345 * <p>A normalized string is a blank-free word where each relevant 346 * substring starts with an upcase character.<br> 347 * 348 * <p>For instance: 349 * 350 * <ul><pre> 351 * - one string --> OneString 352 * - one_string --> OneString 353 * - oneString --> OneString 354 * - one.s-tring --> OneSTring 355 * </pre></ul> 356 * 357 * @param string the string to normalize 358 * @return the normalized string */ 359 360 public static String getNormalizedString(String string) { 361 if ( string.length() == 0 ) return string; 362 StringBuffer sb = new StringBuffer( string.length() ); 363 sb.append ( Character.toUpperCase( string.charAt( 0 ) ) ); 364 boolean wordSep = false; 365 for ( int i = 1; i < string.length(); i++ ) { 366 char c = string.charAt( i ); 367 if ( c == '_' || c == '.' || c == ' ' || c == '-' ) { 368 wordSep = true; 369 } else { 370 if ( wordSep ) { 371 wordSep = false; 372 sb.append ( Character.toUpperCase( c ) ); 373 } else { 374 sb.append ( c ); 375 } 376 } 377 } 378 return new String( sb ); 379 } 380 381 /** 382 * Returns the underscored name for a normalized string. 383 * 384 * <p>A normalized string is a blank-free word where each relevant 385 * substring starts with an upcase character.<br> 386 * 387 * <p>For instance: 388 * 389 * <ul><pre> 390 * - OneString --> one_string 391 * </pre></ul> 392 * 393 * @param string a normalized string 394 * @return the underscored string 395 */ 396 397 public static String getUnderscoredString(String string) { 398 if (string.length() == 0) 399 return string; 400 StringBuffer sb = new StringBuffer(string.length()); 401 sb.append(Character.toLowerCase(string.charAt(0))); 402 for (int i=1; i<string.length(); i++) { 403 char c = string.charAt(i); 404 if (Character.isUpperCase(c)) { 405 sb.append('_'); 406 sb.append(Character.toLowerCase(c)); 407 } else { 408 sb.append(c); 409 } 410 } 411 return new String(sb); 412 } 413 414 /** 415 * Lower case the first character of a string. 416 * 417 * @param string the string to transform 418 * @return the same string but with the first character lowered 419 */ 420 public static String lowerFirst(String string) { 421 if (string.equals("")) 422 return string; 423 char[] chs = string.toCharArray(); 424 chs[0] = Character.toLowerCase(chs[0]); 425 return String.copyValueOf(chs); 426 } 427 428 /** 429 * Lower case the first character of a string unless it starts with 430 * at least two upper case letters. 431 * 432 * @param string the string to transform 433 * @return the same string but with the first character lowered */ 434 public static String maybeLowerFirst(String string) { 435 if (!(string.length()>1 && 436 Character.isUpperCase(string.charAt(0)) && 437 Character.isUpperCase(string.charAt(1)))) 438 return lowerFirst(string); 439 else 440 return string; 441 } 442 443 /** 444 * Get an unprefixed string from a prefixed string.<br> 445 * 446 * <p><b>NOTE:</b> this method is not semantically indentical to 447 * <code>removePrefixFrom</code>. 448 * 449 * <p>For instance: 450 * 451 * <ul><pre> 452 * - getName --> Name 453 * - addName --> Names 454 * - removeName --> Names 455 * </pre></ul> 456 * 457 * @param string a prefixed string 458 * @return the corresponding unprefixed string 459 * @see #removePrefixFrom(String) */ 460 461 public static String getUnprefixedString(String string) { 462 String ret = string; 463 int ps = 0; /* prefix size */ 464 465 if( (ps = isPrefixedWith( string, removerPrefixes )) != 0 ) { 466 ret = getPlural(string.substring(ps)); 467 } else if( (ps = isPrefixedWith( string, adderPrefixes )) != 0 ) { 468 ret = getPlural(string.substring(ps)); 469 } else if( (ps = isPrefixedWith( string, setterPrefixes )) != 0 ) { 470 ret = string.substring(ps); 471 } else if( (ps = isPrefixedWith( string, getterPrefixes )) != 0 ) { 472 ret = string.substring(ps); 473 } 474 475 return ret; 476 } 477 478 /** 479 * Returns the plural of a name 480 * 481 * <p></p> 482 * 483 * @param name the name 484 * @return the plural of name 485 */ 486 public static String getPlural(String name) { 487 if (!name.endsWith("s")) { 488 if (name.endsWith("y")) { 489 return name.substring( 0, name.length()-1 )+"ie"; 490 } else { 491 return name + "s"; 492 } 493 } else { 494 return name + "es"; 495 } 496 } 497 498 /** 499 * Return the singular of a name. Handles english -ies plurals, 500 * and -s. If no plural is recognized, returns name. 501 */ 502 public static String getSingular(String name) { 503 if (name.endsWith("ies")) { 504 return name.substring(0, name.length()-3)+"y"; 505 } else if (name.endsWith("s")) { 506 return name.substring(0, name.length()-1); 507 } else { 508 return name; 509 } 510 } 511 512 /** 513 * Removes the prefix from a prefixed string. 514 * 515 * <p><b>NOTE:</b> this method is not semantically indentical to 516 * <code>getUnprefixedString</code>. 517 * 518 * <p>For instance: 519 * 520 * <ul><pre> 521 * - getName --> Name 522 * - addName --> Name 523 * - removeName --> Name 524 * </pre></ul> 525 * 526 * @param string a prefixed string 527 * @return the corresponding unprefixed string 528 * @see #getUnprefixedString(String) */ 529 530 public static String removePrefixFrom(String string) { 531 String ret = string; 532 int ps = 0; /* prefix size */ 533 534 if( (ps = isPrefixedWith( string, removerPrefixes )) != 0 ) { 535 ret = string.substring( ps ); 536 } else if ( (ps = isPrefixedWith( string, adderPrefixes )) != 0 ) { 537 ret = string.substring( ps ); 538 } else if ( (ps = isPrefixedWith( string, setterPrefixes )) != 0 ) { 539 ret = string.substring( ps ); 540 } else if ( (ps = isPrefixedWith( string, getterPrefixes )) != 0 ) { 541 ret = string.substring( ps ); 542 } 543 544 return ret; 545 } 546 547 /** 548 * Returns the field name for a given method (modifier or getter). 549 * 550 * @param cl the class where the method is supposed to be 551 * @param method the name of the method 552 * @return the field for this method if exist */ 553 554 public static String fieldForMethod(Class cl, String method) { 555 if ( (!isGetter(method)) && (!isModifier(method)) ) 556 return null; 557 String fieldName1 = getUnderscoredString( 558 getUnprefixedString( method ) ); 559 Hashtable fields = ClassRepository.getDirectFieldAccess(cl); 560 if (fields.containsKey(fieldName1)) 561 return fieldName1; 562 String fieldName2 = lowerFirst(getUnprefixedString(method)); 563 if (fields.containsKey(fieldName2)) 564 return fieldName2; 565 return null; 566 } 567 568 /** 569 * Returns the printable textual representation of a field or 570 * method name. 571 * 572 * @param name the field or method name 573 * @return a "natural-language-like" textual representation */ 574 575 public static String textForName(String name) { 576 if (name.length()==0) 577 return name; 578 StringBuffer sb = new StringBuffer(name.length()); 579 sb.append(Character.toUpperCase(name.charAt(0)) ); 580 for (int i = 1; i<name.length(); i++) { 581 char c = name.charAt(i); 582 if ( Character.isUpperCase(c) && 583 ( (i>0) && Character.isLowerCase(name.charAt(i-1)) ) ) { 584 sb.append ( ' ' ); 585 if ( (i<name.length()-1) && 586 Character.isUpperCase(name.charAt(i+1)) ) { 587 sb.append(c); 588 } else { 589 sb.append(Character.toLowerCase(c)); 590 } 591 } else { 592 if ( c == '_' || c == '.' || c == '-' ) { 593 sb.append(' '); 594 } else { 595 sb.append(c); 596 } 597 } 598 } 599 return new String(sb); 600 } 601 602 /** 603 * Returns the normalized name of an aspect regarding its class 604 * name.<p> 605 * 606 * E.g.: 607 * 608 * <ul> 609 * <li>AgendaPersistenceAC -> persistence 610 * 611 * @param name the aspect name to normalize 612 * @return the normalized name */ 613 614 public static String getNormalizedAspectName(String name) { 615 if ( name == null ) return null; 616 if ( name.length() == 0 ) 617 return name; 618 String result = null; 619 StringBuffer sb = new StringBuffer( name.length() ); 620 for ( int i = 1; i < name.length(); i++ ) { 621 char c = name.charAt( i ); 622 if ( Character.isUpperCase( c ) ) { 623 result = name.substring( i ); 624 break; 625 } 626 } 627 if ( result == null ) return null; 628 result = result.substring( 0, result.length() - 2 ); 629 return result.toLowerCase(); 630 } 631 632 /** 633 * Returns the normalized class name of an aspect regarding its 634 * normalized name and the program it belongs to.<p> 635 * 636 * <ul> 637 * <li>org.objectweb.jac.samples.agenda, persistence -> AgendaPersistenceAC 638 * 639 * @param programName name of the application the aspect belongs to 640 * @param aspectName the aspect name to normalize 641 * @return the normalized name */ 642 643 public static String getNormalizedAspectClassName(String programName, 644 String aspectName) { 645 if (aspectName == null || aspectName.length() == 0) 646 return null; 647 String result = null; 648 String shortProgramName = programName.substring( programName.lastIndexOf( '.' ) ); 649 StringBuffer sb1 = new StringBuffer( shortProgramName ); 650 sb1.setCharAt( 1, Character.toUpperCase( sb1.charAt( 1 ) ) ); 651 StringBuffer sb2 = new StringBuffer( aspectName ); 652 sb2.setCharAt( 0, Character.toUpperCase( sb2.charAt( 0 ) ) ); 653 result = programName + new String( sb1 ) + new String( sb2 ) + "AC"; 654 return result; 655 } 656 657 }