GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — javaxt-express/src/javaxt/express/WebService.java at master · javaxt-project/javaxt-express Expand file tree/ Copy pathWebService.javaMore file actions 1103 lines (883 loc) · 40.6 KB / Copy pathWebService.javaFile metadata and controls1103 lines (883 loc) · 40.6 KB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 package javaxt.express; import javaxt.express.ServiceRequest.Sort; import javaxt.express.ServiceRequest.Field; import javaxt.express.ServiceRequest.Filter; import javaxt.express.utils.*; import javaxt.sql.*; import javaxt.json.*; import javaxt.utils.Console; import javaxt.http.servlet.ServletException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; //****************************************************************************** //** WebService //****************************************************************************** /** * Abstract class used to map HTTP requests to either virtual or concrete * methods found in the extending class. * ******************************************************************************/ public abstract class WebService { private ConcurrentHashMap<String, DomainClass> classes = new ConcurrentHashMap<>(); private LinkedHashMap<String, ArrayList<Method>> serviceMethods = new LinkedHashMap<>(); private boolean strictLookup = false; public static Console console = new Console(); //do not replace with static import! private class DomainClass { private Class c; private boolean readOnly; public DomainClass(Class c, boolean readOnly){ this.c = c; this.readOnly = readOnly; } public boolean isReadOnly(){ return readOnly; } public String toString(){ return c.toString() + (readOnly ? " (readonly)" : ""); } } //************************************************************************** //** Constructor //************************************************************************** public WebService(){ //Generate a list of all the service methods in the subclass. Service //methods are public methods that accept a ServiceRequest parameter and //return a ServiceResponse object. Implementation note: //The getDeclaredMethod() method will only find methods declared in the //current Class, not inherited from supertypes. So we may need to //traverse up the concrete class hierarchy if this becomes a requirement. for (Method m : this.getClass().getDeclaredMethods()){ if (Modifier.isPrivate(m.getModifiers())) continue; if (m.getReturnType().equals(ServiceResponse.class)){ Class<?>[] params = m.getParameterTypes(); if (params.length>0){ if (ServiceRequest.class.isAssignableFrom(params[0])){ String key = m.getName(); if (!strictLookup) key = key.toLowerCase(); ArrayList<Method> methods = serviceMethods.get(key); if (methods==null){ methods = new ArrayList<>(); serviceMethods.put(key, methods); } methods.add(m); } } } } //Experimental special case for anonymous classes if (this.getClass().isAnonymousClass()){ for (Method m : this.getClass().getMethods()){ if (Modifier.isPrivate(m.getModifiers())) continue; if (m.getReturnType().equals(ServiceResponse.class)){ Class<?>[] params = m.getParameterTypes(); if (params.length>0){ if (ServiceRequest.class.isAssignableFrom(params[0])){ String key = m.getName(); if (!strictLookup) key = key.toLowerCase(); ArrayList<Method> methods = serviceMethods.get(key); if (methods==null){ methods = new ArrayList<>(); serviceMethods.put(key, methods); methods.add(m); } } } } } } } //************************************************************************** //** addModel //************************************************************************** /** Register model that this service will support * @param c A Java class that extends the javaxt.sql.Model abstract class. */ public void addModel(Class c){ addModel(c, false); } //************************************************************************** //** addModel //************************************************************************** /** Register model that this service will support * @param c A Java class that extends the javaxt.sql.Model abstract class. * @param readOnly If true, the CRUD operations will be disabled. PUT, * POST, and DELETE requests will be handled the same as GET requests. */ public void addModel(Class c, boolean readOnly){ if (!Model.class.isAssignableFrom(c)){ throw new IllegalArgumentException(c + " is not a javaxt.sql.Model"); } String name = c.getSimpleName(); String pkg = c.getPackage().getName(); if (name.startsWith(pkg)) name = name.substring(pkg.length()+1); name = name.toLowerCase(); int idx = name.lastIndexOf("."); if (idx>0) name = name.substring(idx+1); synchronized(classes){ classes.put(name, new DomainClass(c, readOnly)); classes.notify(); } } //************************************************************************** //** addClass //************************************************************************** /** @deprecated Use addModel instead */ public void addClass(Class c){ addModel(c); } //************************************************************************** //** addClass //************************************************************************** /** @deprecated Use addModel instead */ public void addClass(Class c, boolean readOnly){ addModel(c, readOnly); } //************************************************************************** //** getServiceResponse //************************************************************************** /** Returns a ServiceResponse for a given request. */ public ServiceResponse getServiceResponse(ServiceRequest request) throws ServletException { return getServiceResponse(request, null); } //************************************************************************** //** getServiceResponse //************************************************************************** /** Returns a ServiceResponse for a given request and database. */ public ServiceResponse getServiceResponse(ServiceRequest request, Database database) throws ServletException { //Get requested method. Note that the ServiceRequest typically prepends a //keyword to the request path (e.g. get, save, delete) depending on the //HTTP request method (e.g. GET, POST, PUT, DELETE) String methodName = request.getMethod(); //Find a concrete implementation of the requested method in the subclass if (!strictLookup) methodName = methodName.toLowerCase(); ArrayList<Method> methods = null; if (serviceMethods.containsKey(methodName)){ methods = serviceMethods.get(methodName); } else{ int i = 0; if (methodName.startsWith("get")) i = 4; if (methodName.startsWith("save")) i = 5; if (methodName.startsWith("delete")) i = 6; if (i>0){ //Check for a simple method like search() or update() without a //"get" or "save" or "delete" prefix methodName = methodName.substring(i-1, i).toLowerCase() + methodName.substring(i); methods = serviceMethods.get(methodName); //Special case for POST requests (e.g. "POST /companies") that //map to a "save" method (e.g. saveCompanies) that does not exist. //Let's check if there's a suitable "get" method instead (e.g. getCompanies). if (methods==null && i==5){ // && methodName.endsWith("s") methods = serviceMethods.get("get" + methodName); } } } //Return ServiceResponse if (methods!=null){ for (Method m : methods){ Class<?>[] params = m.getParameterTypes(); //Check whether the method accepts a ServiceRequest //or ServiceRequest + Database as inputs Object[] inputs = null; if (params.length==1){ inputs = new Object[]{request}; } else if (params.length==2){ if (Database.class.isAssignableFrom(params[1])){ inputs = new Object[]{request, database}; } } if (inputs!=null){ //Ensure that we don't want to invoke this function! //For example, the caller might want to call //super.getServiceResponse(request, database); //If so, we would end up in a recursion causing a stack //overflow. Instead of calling getServiceResponse() //let's just flow down to the CRUD handlers below. StackTraceElement[] stackTrace = new Exception().getStackTrace(); StackTraceElement el = stackTrace[1]; if (m.getName().equals(el.getMethodName())){ break; } //If we're still here, call the requested method and return //the response try{ m.setAccessible(true); return (ServiceResponse) m.invoke(this, inputs); } catch(Exception e){ return getServiceResponse(e); } } } } //If we're still here, see if the requested method corresponds to a //standard CRUD operation. String method = request.getMethod().toLowerCase(); //don't use methodName! if (method.startsWith("get")){ //Find and return model String className = method.substring(3); DomainClass c = getClass(className); if (c!=null) return get(c.c, request, database); //Special case for plural-form of a model. Return list of models. c = getClassFromPluralName(className); if (c!=null) return list(c.c, request, database); } else if (method.startsWith("save")){ //Find model and save String className = method.substring(4); DomainClass c = getClass(className); if (c!=null){ if (c.isReadOnly()){ return get(c.c, request, database); } else{ return save(c.c, request, database); } } //Special case for plural-form of a model. Return list of models. c = getClassFromPluralName(className); if (c!=null) return list(c.c, request, database); } else if (method.startsWith("delete")){ String className = method.substring(6); DomainClass c = getClass(className); if (c!=null){ if (c.isReadOnly()){ return new ServiceResponse(403, "Delete access forbidden."); } else{ return delete(c.c, request, database); } } } return new ServiceResponse(501, "Not Implemented."); } //************************************************************************** //** getRecordset //************************************************************************** /** Returns a Recordset that is used fetch records from the database and * support CRUD operations. This is a protected method that extending * classes can override to apply custom filters or add constraints when * retrieving objects from the database. This method is called whenever an * HTTP GET, POST, or DELETE request is made for a Model. It is perfectly * acceptable to throw exceptions when overriding this method. When * throwing exceptions, an IllegalArgumentException will return a HTTP 400 * error to the client and a SecurityException will return a 403 error. All * other exceptions will return a 500 error. * @param op Operation that is requesting the Recordset. Options include * "list, "get", "save", and "delete". * @param c The Model (Java class) associated with the request. * @param sql The default SQL statement generated for the request. * @param conn A database connection used to open the Recordset. */ protected Recordset getRecordset(ServiceRequest request, String op, Class c, String sql, Connection conn) throws Exception { Recordset rs = new Recordset(); if (op.equals("list")) rs.setFetchSize(1000); rs.open(sql, conn); return rs; } //************************************************************************** //** get //************************************************************************** /** Used to retrieve an object from the database. Returns a JSON object. */ private ServiceResponse get(Class c, ServiceRequest request, Database database) { try{ //Compile sql statement HashMap<String, Object> tablesAndFields = getTableAndFields(c); String tableName = (String) tablesAndFields.get("tableName"); String sql = "select " + tableName + ".id from " + tableName + " where "; Long id = request.getID(); if (id==null){ String where = request.getWhereStatement(tablesAndFields); if (where==null) return new ServiceResponse(404); else sql += where; } else{ sql += tableName + ".id=" + id; } //Apply filter try (Connection conn = database.getConnection()){ try (Recordset rs = getRecordset(request, "get", c, sql, conn)){ if (rs.EOF) id = null; else id = rs.getValue("id").toLong(); } } if (id==null) return new ServiceResponse(404); Object obj = newInstance(c, id); Method toJson = getMethod("toJson", c); return new ServiceResponse((JSONObject) toJson.invoke(obj)); } catch(Exception e){ return getServiceResponse(e); } } //************************************************************************** //** list //************************************************************************** /** Used to retrieve a shallow list of objects from the database. */ private ServiceResponse list(Class c, ServiceRequest request, Database database){ //Get tableName and fields associated with the Model HashMap<String, Object> tablesAndFields; HashSet<String> spatialFields; String tableName; try{ tablesAndFields = getTableAndFields(c); tableName = (String) tablesAndFields.get("tableName"); spatialFields = (HashSet<String>) tablesAndFields.get("spatialFields"); } catch(Exception e){ return getServiceResponse(e); } //Compile SQL statement StringBuilder sql = new StringBuilder("select "); sql.append(request.getSelectStatement(tablesAndFields)); sql.append(" from "); sql.append(tableName); String where = request.getWhereStatement(tablesAndFields); if (where!=null){ sql.append(" where "); sql.append(where); } //Add order by statement Sort sort = request.getSort(); if (!sort.isEmpty()){ sql.append(" order by "); boolean addID = true; java.util.Iterator<String> it = sort.getKeySet().iterator(); while (it.hasNext()){ String colName = it.next(); String direction = sort.get(colName); sql.append(colName); sql.append(" "); sql.append(direction); if (it.hasNext()) sql.append(", "); if (colName.equalsIgnoreCase("id")) addID = false; } //Add ID (unique primary key) to the order by statement as needed. //This is important when paginating sorting on non-distinct columns, //especially on H2. if (addID) sql.append(", id"); } sql.append(request.getOffsetLimitStatement(database.getDriver())); //console.log(sql); //Get output format String format = request.getFormat(); //Excute query and generate response try (Connection conn = database.getConnection()){ try (Recordset rs = getRecordset(request, "list", c, sql.toString(), conn)){ ServiceResponse response; if (format.equals("csv")){ StringBuilder csv = new StringBuilder(); long x = 0; while (rs.next()){ if (x>0) csv.append("\r\n"); //Add header row as needed if (x==0){ int i = 0; HashSet<String> fieldNames = new HashSet<>(); for (javaxt.sql.Field field : rs.getFields()){ String fieldName = field.getName().toLowerCase(); if (fieldNames.contains(fieldName)) continue; fieldNames.add(fieldName); if (i>0) csv.append(","); csv.append(fieldName); i++; } csv.append("\r\n"); } //Add data row int i = 0; HashSet<String> fieldNames = new HashSet<>(); for (javaxt.sql.Field field : rs.getFields()){ String fieldName = field.getName().toLowerCase(); if (fieldNames.contains(fieldName)) continue; fieldNames.add(fieldName); if (i>0) csv.append(","); javaxt.sql.Value value = field.getValue(); if (!value.isNull()){ String val = value.toString(); //Update spatial data as needed fieldName = StringUtils.underscoreToCamelCase(fieldName); if (spatialFields.contains(fieldName)){ if (database.getDriver().equals("PostgreSQL")){ val = createGeom(val.toString()).toString(); } } if (val.contains("\"") || val.contains(",")){ val = "\"" + val + "\""; } csv.append(val); } i++; } x++; } response = new ServiceResponse(csv); response.setContentType("text/csv"); } else if (format.equals("json")){ StringBuilder arr = new StringBuilder("["); long x = 0; while (rs.next()){ JSONObject json = DbUtils.getJson(rs); //Update spatial data as needed for (String fieldName : json.keySet()){ JSONValue val = json.get(fieldName); if (!val.isNull()){ if (spatialFields.contains(fieldName)){ if (database.getDriver().equals("PostgreSQL")){ val = new JSONValue(createGeom(val.toString())); json.set(fieldName, val); } } } } if (x>0) arr.append(","); arr.append(json); x++; } arr.append("]"); response = new ServiceResponse(arr.toString()); response.setContentType("application/json"); } else { long x = 0; JSONArray cols = new JSONArray(); StringBuilder json = new StringBuilder("{\"rows\":["); while (rs.next()){ JSONArray row = new JSONArray(); HashSet<String> fieldNames = new HashSet<>(); for (javaxt.sql.Field field : rs.getFields()){ String fieldName = field.getName().toLowerCase(); fieldName = StringUtils.underscoreToCamelCase(fieldName); if (fieldNames.contains(fieldName)) continue; fieldNames.add(fieldName); if (x==0) cols.add(fieldName); JSONObject f = field.toJson(); JSONValue val = f.get("value"); //Update spatial data as needed if (!val.isNull()){ if (spatialFields.contains(fieldName)){ if (database.getDriver().equals("PostgreSQL")){ val = new JSONValue(createGeom(val.toString())); } } } row.add(val); } if (x>0) json.append(","); json.append(row.toString()); x++; } json.append("]"); //Append columns json.append(",\"cols\":"); json.append(cols.toString()); //Append count as needed if (request.getCount()){ rs.close(); javaxt.sql.Record r = conn.getRecord( "select count(id) from " + tableName + (where==null ? "" : " where " + where) ); if (r!=null){ json.append(",\"count\":"); json.append(r.get(0).toLong()); } } json.append("}"); response = new ServiceResponse(json.toString()); response.setContentType("application/json"); } return response; } } catch(Exception e){ return getServiceResponse(e); } } //************************************************************************** //** save //************************************************************************** /** Used to create or update an object in the database. Returns the object * ID. */ private ServiceResponse save(Class c, ServiceRequest request, Database database) { try{ //Parse json JSONObject json = request.getJson(); if (json==null || json.isEmpty()) throw new Exception("JSON is empty."); Long id = json.get("id").toLong(); boolean isNew = id==null; //Apply filter HashMap<String, Object> tablesAndFields = getTableAndFields(c); String tableName = (String) tablesAndFields.get("tableName"); String sql = "select " + tableName + ".id from " + tableName + " where " + tableName + ".id=" + (id==null ? -1 : id); try (Connection conn = database.getConnection()){ try (Recordset rs = getRecordset(request, "save", c, sql, conn)){ if (rs.EOF) id = null; else id = rs.getValue("id").toLong(); } } if (id==null && !isNew) return new ServiceResponse(404); //Reparse json (json may have changed in getRecordset) json = request.getJson(); id = json.get("id").toLong(); isNew = id==null; //Create new instance of the class Object obj; if (id!=null){ obj = newInstance(c, id); beforeUpdate(obj, request); Method update = c.getDeclaredMethod("update", JSONObject.class); update.invoke(obj, new Object[]{json}); } else{ obj = newInstance(c, json); beforeCreate(obj, request); isNew = true; } //Call the save method Method save = getMethod("save", c); save.invoke(obj); //Get id Method getID = getMethod("getID", c); id = (Long) getID.invoke(obj); if (id==null) return new ServiceResponse(500, "Failed to retrieve ID on save"); //Fire event if (isNew) onCreate(obj, request); else onUpdate(obj, request); //Return response return new ServiceResponse(id+""); } catch(Exception e){ return getServiceResponse(e); } } //************************************************************************** //** delete //************************************************************************** /** Used to delete an object in the database. Returns a JSON representation * of the object that was deleted. */ private ServiceResponse delete(Class c, ServiceRequest request, Database database) { try (Connection conn = database.getConnection()){ //Apply filter Long id = request.getID(); try (Recordset rs = getRecordset(request, "delete", c, "select id from " + getTableName(c.newInstance()) + " where id=" + id, conn)){ if (rs.EOF) id = null; else id = rs.getValue("id").toLong(); } if (id==null) return new ServiceResponse(404); //Reparse request to get ID (id may have changed in getRecordset) Long newID = request.getParameter("id").toLong(); if (newID!=null) id = newID; //Create new instance of the class Object obj = newInstance(c, id); //Fire event beforeDelete(obj, request); //Delete object Method delete = getMethod("delete", c); delete.invoke(obj); //Fire event onDelete(obj, request); //Return response Method toJson = getMethod("toJson", c); JSONObject json = (JSONObject) toJson.invoke(obj); return new ServiceResponse(json); } catch(Exception e){ return getServiceResponse(e); } } //************************************************************************** //** beforeCreate //************************************************************************** /** This method is called immediately before a record is inserted into the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void beforeCreate(Object obj, ServiceRequest request){}; //************************************************************************** //** onCreate //************************************************************************** /** This method is called immediately after a record is inserted into the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void onCreate(Object obj, ServiceRequest request){}; //************************************************************************** //** beforeUpdate //************************************************************************** /** This method is called immediately before a record is updated in the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void beforeUpdate(Object obj, ServiceRequest request){}; //************************************************************************** //** onUpdate //************************************************************************** /** This method is called immediately after a record is updated in the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void onUpdate(Object obj, ServiceRequest request){}; //************************************************************************** //** beforeDelete //************************************************************************** /** This method is called immediately before a record is deleted in the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void beforeDelete(Object obj, ServiceRequest request){}; //************************************************************************** //** onDelete //************************************************************************** /** This method is called immediately after a record is deleted in the * database. Override this method to process the event. * @param obj Object. If this method is called by this class, the Object * will correspond to an instance of a javaxt.sql.Model */ public void onDelete(Object obj, ServiceRequest request){}; //************************************************************************** //** getClass //************************************************************************** /** Returns a class from the list of known/supported classes for a given * class name. Performs an exact match. */ private DomainClass getClass(String className){ synchronized(classes){ return classes.get(className); } } //************************************************************************** //** getClassFromPluralName //************************************************************************** /** Returns a class from the list of known/supported classes for a plural * form of a class name. */ private DomainClass getClassFromPluralName(String className){ if (className==null) return null; //Check for common plural suffixes if (className.endsWith("ies")){ //Categories == Category if (className.length() > 3){ char beforeIes = className.charAt(className.length()-4); if (!"aeiou".contains(String.valueOf(beforeIes).toLowerCase())){ return getClass(className.substring(0, className.length()-3) + "y"); } } } else if (className.endsWith("sses")){ //Classes == Class return getClass(className.substring(0, className.length()-2)); } else if (className.endsWith("ches")){ //Matches == Match return getClass(className.substring(0, className.length()-2)); } else if (className.endsWith("shes")){ //Dishes == Dish return getClass(className.substring(0, className.length()-2)); } else if (className.endsWith("xes")){ //Boxes == Box return getClass(className.substring(0, className.length()-2)); } else if (className.endsWith("zzes")){ //Buzzes == Buzz return getClass(className.substring(0, className.length()-2)); } else if (className.endsWith("ses")){ //Viruses = Virus, Gases = Gas return getClass(className.substring(0, className.length()-2)); } //If we're still here, try stripping out the last "s" if (className.endsWith("s")){ //Sources == Source, Premises = Premise return getClass(className.substring(0, className.length()-1)); } //If we're still here, check for common irregular plurals if (className.equalsIgnoreCase("men")) return getClass("Man"); if (className.equalsIgnoreCase("women")) return getClass("Woman"); if (className.equalsIgnoreCase("people")) return getClass("Person"); if (className.equalsIgnoreCase("children")) return getClass("Child"); if (className.equalsIgnoreCase("indices")) return getClass("Index"); if (className.equalsIgnoreCase("vertices")) return getClass("Vertex"); if (className.equalsIgnoreCase("analyses")) return getClass("Analysis"); if (className.equalsIgnoreCase("criteria")) return getClass("Criterion"); return null; } //************************************************************************** //** getMethod //************************************************************************** /** Returns a declared (public) method defined in a given class. */ private Method getMethod(String name, Class clazz){ while (clazz != null) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(name)) { return method; } } clazz = clazz.getSuperclass(); } return null; } //************************************************************************** //** newInstance //************************************************************************** /** Returns a new instance for a given class using an ID and a connection to * the database. */ private Object newInstance(Class c, long id) throws Exception { Constructor constructor = c.getDeclaredConstructor(new Class[]{Long.TYPE}); return constructor.newInstance(new Object[]{id}); } //************************************************************************** //** newInstance //************************************************************************** /** Returns a new instance for a given class using a JSON object. */ private Object newInstance(Class c, JSONObject json) throws Exception { Constructor constructor = c.getDeclaredConstructor(new Class[]{JSONObject.class}); return constructor.newInstance(new Object[]{json}); } //************************************************************************** //** getTableName //************************************************************************** /** Returns the "tableName" private variable associated with a model */ private String getTableName(Object obj) throws Exception { java.lang.reflect.Field field = obj.getClass().getSuperclass().getDeclaredField("tableName"); field.setAccessible(true); String tableName = (String) field.get(obj); return tableName; } //************************************************************************** //** getTableAndFields //************************************************************************** /** Returns a HashMap with the table name and fields associated with a given * model. */ protected static HashMap<String, Object> getTableAndFields(Class c) throws Exception { String tableName; HashMap<String, String> fieldMap = new HashMap<>(); HashSet<String> stringFields = new HashSet<>(); HashSet<String> arrayFields = new HashSet<>(); HashSet<String> spatialFields = new HashSet<>(); Object obj = c.newInstance(); //maybe clone instead? //Get tableName java.lang.reflect.Field field = obj.getClass().getSuperclass().getDeclaredField("tableName"); field.setAccessible(true); tableName = (String) field.get(obj); //Get fieldMap field = obj.getClass().getSuperclass().getDeclaredField("fieldMap"); field.setAccessible(true); HashMap<String, String> map = (HashMap<String, String>) field.get(obj); Iterator<String> it = map.keySet().iterator(); while (it.hasNext()){ String fieldName = it.next(); String columnName = map.get(fieldName); |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |