PageRenderTime 60ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/web.client.rest/src/org/netbeans/modules/web/client/rest/wizard/ModelGenerator.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 558 lines | 457 code | 44 blank | 57 comment | 89 complexity | 72caa185364f2a7a85d49a6984c6faf2 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. * The Original Software is NetBeans. The Initial Developer of the Original
  29. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  30. * Microsystems, Inc. All Rights Reserved.
  31. *
  32. * If you wish your version of this file to be governed by only the CDDL
  33. * or only the GPL Version 2, indicate your decision by adding
  34. * "[Contributor] elects to include this software in this distribution
  35. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  36. * single choice of license, a recipient has the option to distribute
  37. * your version of this file under either the CDDL, the GPL Version 2 or
  38. * to extend the choice of license to its licensees as provided above.
  39. * However, if you add GPL Version 2 code and therefore, elected the GPL
  40. * Version 2 license, then the option applies only if the new code is
  41. * made subject to such option by the copyright holder.
  42. */
  43. package org.netbeans.modules.web.client.rest.wizard;
  44. import java.io.IOException;
  45. import java.util.EnumSet;
  46. import java.util.HashMap;
  47. import java.util.HashSet;
  48. import java.util.List;
  49. import java.util.Map;
  50. import java.util.Set;
  51. import java.util.Map.Entry;
  52. import javax.lang.model.element.Element;
  53. import javax.lang.model.element.ExecutableElement;
  54. import javax.lang.model.element.Modifier;
  55. import javax.lang.model.element.PackageElement;
  56. import javax.lang.model.element.TypeElement;
  57. import javax.lang.model.element.VariableElement;
  58. import javax.lang.model.type.TypeKind;
  59. import javax.lang.model.type.TypeMirror;
  60. import javax.lang.model.util.ElementFilter;
  61. import org.netbeans.api.java.source.CompilationController;
  62. import org.netbeans.api.project.FileOwnerQuery;
  63. import org.netbeans.api.project.Project;
  64. import org.netbeans.modules.web.client.rest.wizard.JSClientGenerator.HttpRequests;
  65. import org.netbeans.modules.web.client.rest.wizard.JSClientGenerator.MethodType;
  66. import org.netbeans.modules.web.client.rest.wizard.RestPanel.JsUi;
  67. import org.netbeans.modules.websvc.rest.model.api.RestServiceDescription;
  68. import org.netbeans.modules.websvc.rest.spi.RestSupport;
  69. /**
  70. * @author ads
  71. *
  72. */
  73. class ModelGenerator {
  74. private static final String SLASH = "/"; // NOI18N
  75. private static final String ID = "javax.persistence.Id"; // NOI18N
  76. ModelGenerator(RestServiceDescription description ,
  77. StringBuilder builder, Set<String> entities , JsUi ui)
  78. {
  79. myDescription = description;
  80. myCommonModels = builder;
  81. myEntities = entities;
  82. myUi = ui;
  83. }
  84. void generateModel(TypeElement entity, String path,
  85. String collectionPath, Map<HttpRequests, String> httpPaths ,
  86. Map<HttpRequests, Boolean> useIds,
  87. CompilationController controller ) throws IOException
  88. {
  89. String fqn = entity.getQualifiedName().toString();
  90. String name = entity.getSimpleName().toString();
  91. myModelName = suggestModelName(name );
  92. myCommonModels.append("\n// Model for "); // NOI18N
  93. if ( name.equals(myModelName)){
  94. myCommonModels.append( name );
  95. }
  96. else {
  97. myCommonModels.append( fqn );
  98. }
  99. myCommonModels.append(" entity\n"); // NOI18N
  100. String url = getUrl( path );
  101. myCommonModels.append("models."); // NOI18N
  102. myCommonModels.append(myModelName);
  103. myCommonModels.append(" = Backbone.Model.extend({\n"); // NOI18N
  104. myCommonModels.append("urlRoot : \""); // NOI18N
  105. myCommonModels.append( url );
  106. myCommonModels.append("\""); // NOI18N
  107. myAttributes = new HashSet<ModelAttribute>();
  108. String parsedData = parse(entity, controller);
  109. if ( parsedData != null ){
  110. myCommonModels.append(',');
  111. myCommonModels.append(parsedData);
  112. }
  113. if ( !myAttributes.isEmpty() ){
  114. // suggest what attribute could be used as displayName
  115. ModelAttribute preffered = ModelAttribute.getPreffered();
  116. if ( myAttributes.contains( preffered )){
  117. myDisplayNameAlias = preffered.getName();
  118. }
  119. else if ( myIdAttribute == null){
  120. myDisplayNameAlias = myAttributes.iterator().next().getName();
  121. }
  122. else {
  123. myDisplayNameAlias = myIdAttribute.getName();
  124. }
  125. myCommonModels.append(",\n toViewJson: function(){\n"); // NOI18N
  126. myCommonModels.append("var result = this.toJSON();"); // NOI18N
  127. myCommonModels.append(" // displayName property is used to render item in the list\n");// NOI18N
  128. myCommonModels.append("result.displayName = this.get('"); // NOI18N
  129. myCommonModels.append(myDisplayNameAlias);
  130. myCommonModels.append("');\n return result;\n},\n"); // NOI18N
  131. myCommonModels.append("isNew: function(){\n"); // NOI18N
  132. myCommonModels.append(" // default isNew() method imlementation is\n");// NOI18N
  133. myCommonModels.append(" // based on the 'id' initialization which\n" );// NOI18N
  134. myCommonModels.append(" // sometimes is required to be initialized.\n");// NOI18N
  135. myCommonModels.append(" // So isNew() is rediefined here\n"); // NOI18N
  136. myCommonModels.append("return this.notSynced;\n}"); // NOI18N
  137. }
  138. else if ( myIdAttribute != null){
  139. myDisplayNameAlias = myIdAttribute.getName();
  140. }
  141. String sync = overrideSync( url, httpPaths , useIds);
  142. if ( sync != null && sync.length()>0 ){
  143. myCommonModels.append(",\n"); // NOI18N
  144. myCommonModels.append(sync);
  145. myCommonModels.append("\n"); // NOI18N
  146. }
  147. myCommonModels.append("\n});\n\n"); // NOI18N
  148. if ( collectionPath == null){
  149. return;
  150. }
  151. myCommonModels.append("\n // Collection class for "); // NOI18N
  152. if ( name.equals(myModelName)){
  153. myCommonModels.append( name );
  154. }
  155. else {
  156. myCommonModels.append( entity.getQualifiedName().toString() );
  157. }
  158. myCommonModels.append(" entities\n"); // NOI18N
  159. myCommonModels.append("models.");
  160. StringBuilder builder = new StringBuilder(myModelName);
  161. builder.append("Collection"); // NOI18N
  162. myCollectionModelName = builder.toString();
  163. myCommonModels.append(myCollectionModelName);
  164. myCommonModels.append(" = Backbone.Collection.extend({\n"); // NOI18N
  165. myCommonModels.append("model: models."); // NOI18N
  166. myCommonModels.append(myModelName);
  167. myCommonModels.append(",\nurl : \""); // NOI18N
  168. myCommonModels.append( getUrl( collectionPath ));
  169. myCommonModels.append("\",\n"); // NOI18N
  170. myCommonModels.append( getModifierdSync(""));
  171. myCommonModels.append("});\n\n"); // NOI18N
  172. }
  173. JsUi getUi(){
  174. return myUi;
  175. }
  176. boolean hasCollection(){
  177. return myCollectionModelName!= null;
  178. }
  179. Set<ModelAttribute> getAttributes(){
  180. return myAttributes;
  181. }
  182. String getDisplayNameAlias(){
  183. return myDisplayNameAlias;
  184. }
  185. String getModelName(){
  186. return myModelName;
  187. }
  188. String getCollectionModelName(){
  189. return myCollectionModelName;
  190. }
  191. ModelAttribute getIdAttribute(){
  192. return myIdAttribute;
  193. }
  194. private String overrideSync( String url,
  195. Map<HttpRequests, String> httpPaths,
  196. Map<HttpRequests, Boolean> useIds ) throws IOException
  197. {
  198. StringBuilder builder = new StringBuilder();
  199. for( Entry<HttpRequests,String> entry : httpPaths.entrySet() ){
  200. overrideMethod(url, entry.getValue(),
  201. useIds.get(entry.getKey()), entry.getKey(), builder);
  202. }
  203. EnumSet<HttpRequests> set = EnumSet.allOf(HttpRequests.class);
  204. set.removeAll( httpPaths.keySet());
  205. for( HttpRequests request : set ){
  206. overrideMethod(url, null, null, request, builder);
  207. }
  208. return getModifierdSync( builder.toString() ).toString();
  209. }
  210. private String getModifierdSync( String body ){
  211. StringBuilder result = new StringBuilder();
  212. result.append( "sync: function(method, model, options){\n"); // NOI18N
  213. result.append("options || (options = {});\n"); // NOI18N
  214. result.append("var errorHandler = {\n"); // NOI18N
  215. result.append("error: function (jqXHR, textStatus, errorThrown){\n"); // NOI18N
  216. result.append(" // TODO: put your error handling code here\n"); // NOI18N
  217. result.append(" // If you use the JS client from the different domain\n");// NOI18N
  218. result.append(" // (f.e. locally) then Cross-origin resource sharing \n");// NOI18N
  219. result.append(" // headers has to be set on the REST server side.\n"); // NOI18N
  220. result.append(" // Otherwise the JS client has to be copied into the\n");// NOI18N
  221. result.append(" // some (f.e. the same) Web project on the same domain\n");// NOI18N
  222. result.append("alert('Unable to fulfil the request');\n}}\n\n"); // NOI18N
  223. result.append( body );
  224. result.append("var result = Backbone.sync(method, model, "); // NOI18N
  225. result.append("_.extend(options,errorHandler));\n"); // NOI18N
  226. result.append("return result;\n}\n");
  227. return result.toString();
  228. }
  229. private String getUrl( String relativePath ) throws IOException {
  230. Project project = FileOwnerQuery.getOwner(myDescription.getFile());
  231. RestSupport restSupport = project.getLookup().lookup(RestSupport.class);
  232. String applicationPath = restSupport.getApplicationPath();
  233. String uri = myDescription.getUriTemplate();
  234. if (applicationPath == null) {
  235. applicationPath = uri;
  236. }
  237. else {
  238. applicationPath = addUrlPath(applicationPath, uri);
  239. }
  240. applicationPath = addUrlPath(applicationPath, relativePath);
  241. return addUrlPath(restSupport.getContextRootURL(),applicationPath);
  242. }
  243. private String suggestModelName( String name ) {
  244. if ( myEntities.contains(name)){
  245. String newName ;
  246. int index =1;
  247. while( true ){
  248. newName = name+index;
  249. if ( !myEntities.contains(newName)){
  250. myEntities.add(newName);
  251. return newName;
  252. }
  253. index++;
  254. }
  255. }
  256. else {
  257. myEntities.add(name);
  258. }
  259. return name;
  260. }
  261. private String parse( TypeElement entity, CompilationController controller )
  262. {
  263. /*
  264. * parse entity and generate attributes:
  265. * 1) idAttribute
  266. * 2) primitive attributes if any
  267. * 3) do not include attributes with complex type
  268. */
  269. Set<String> attributes = parseBeanMethods( entity , controller );
  270. List<VariableElement> fields = ElementFilter.fieldsIn(
  271. controller.getElements().getAllMembers(entity));
  272. VariableElement id = null;
  273. for (VariableElement field : fields) {
  274. if ( JSClientGenerator.getAnnotation(field, ID) != null ){
  275. boolean has = attributes.remove(field.getSimpleName().toString());
  276. if ( has ){
  277. id = field;
  278. break;
  279. }
  280. }
  281. }
  282. StringBuilder builder = new StringBuilder();
  283. if ( id != null ){
  284. String idAttr = id.getSimpleName().toString();
  285. builder.append("\nidAttribute : '"); // NOI18N
  286. builder.append(idAttr);
  287. builder.append("'"); // NOI18N
  288. if ( attributes.size() >0 ){
  289. builder.append(',');
  290. }
  291. myIdAttribute = new ModelAttribute(idAttr);
  292. }
  293. if (attributes.size() > 0) {
  294. builder.append("\ndefaults: {"); // NOI18N
  295. for (String attribute : attributes) {
  296. myAttributes.add( new ModelAttribute(attribute));
  297. builder.append("\n"); // NOI18N
  298. builder.append(attribute);
  299. builder.append(": \"\","); // NOI18N
  300. }
  301. builder.deleteCharAt(builder.length()-1);
  302. builder.append("\n}"); // NOI18N
  303. }
  304. if ( builder.length() >0 ){
  305. return builder.toString();
  306. }
  307. else {
  308. return null;
  309. }
  310. }
  311. private Set<String> parseBeanMethods( TypeElement entity,
  312. CompilationController controller )
  313. {
  314. List<ExecutableElement> methods = ElementFilter.methodsIn(
  315. controller.getElements().getAllMembers(entity));
  316. Set<String> result = new HashSet<String>();
  317. Map<String,TypeMirror> getAttrs = new HashMap<String, TypeMirror>();
  318. Map<String,TypeMirror> setAttrs = new HashMap<String, TypeMirror>();
  319. for (ExecutableElement method : methods) {
  320. if ( !method.getModifiers().contains( Modifier.PUBLIC)){
  321. continue;
  322. }
  323. Object[] attribute = getAttrName( method , controller);
  324. if ( attribute == null ){
  325. continue;
  326. }
  327. String name = (String)attribute[1];
  328. TypeMirror type = (TypeMirror)attribute[2];
  329. if ( attribute[0] == MethodType.GET ){
  330. if ( findAccessor(name, type, getAttrs, setAttrs, controller)){
  331. result.add(name);
  332. }
  333. }
  334. else {
  335. if ( findAccessor(name, type, setAttrs, getAttrs, controller)){
  336. result.add(name);
  337. }
  338. }
  339. }
  340. return result;
  341. }
  342. private boolean findAccessor(String name, TypeMirror type,
  343. Map<String,TypeMirror> map1, Map<String,TypeMirror> map2,
  344. CompilationController controller)
  345. {
  346. TypeMirror typeMirror = map2.remove(name);
  347. if ( typeMirror!= null &&
  348. controller.getTypes().isSameType(typeMirror, type))
  349. {
  350. return true;
  351. }
  352. else {
  353. map1.put(name, type);
  354. }
  355. return false;
  356. }
  357. private Object[] getAttrName( ExecutableElement method,
  358. CompilationController controller )
  359. {
  360. String name = method.getSimpleName().toString();
  361. if ( name.startsWith("set") ){ // NOI18N
  362. TypeMirror returnType = method.getReturnType();
  363. if ( returnType.getKind()!= TypeKind.VOID){
  364. return null;
  365. }
  366. List<? extends VariableElement> parameters = method.getParameters();
  367. if ( parameters.size() !=1 ){
  368. return null;
  369. }
  370. VariableElement param = parameters.get(0);
  371. TypeMirror type = param.asType();
  372. if ( isSimple(type, controller)){
  373. return new Object[]{MethodType.SET, lowerFirstLetter(
  374. name.substring(3)), type};
  375. }
  376. else {
  377. return null;
  378. }
  379. }
  380. int start =0;
  381. if ( name.startsWith("get")){ // NOI18N
  382. start =3;
  383. }
  384. else if ( name.startsWith( "is")){ // NOI18N
  385. start =2;
  386. }
  387. if ( start > 0){
  388. List<? extends VariableElement> parameters = method.getParameters();
  389. if ( parameters.size() !=0 ){
  390. return null;
  391. }
  392. TypeMirror returnType = method.getReturnType();
  393. if ( isSimple(returnType, controller)){
  394. return new Object[]{ MethodType.GET, lowerFirstLetter(
  395. name.substring(start)), returnType};
  396. }
  397. else {
  398. return null;
  399. }
  400. }
  401. return null;
  402. }
  403. private String lowerFirstLetter( String name ){
  404. if ( name.length() <=1){
  405. return name;
  406. }
  407. char firstLetter = name.charAt(0);
  408. if ( Character.isUpperCase(firstLetter)){
  409. return Character.toLowerCase(firstLetter) +name.substring(1);
  410. }
  411. return name;
  412. }
  413. /*
  414. * returns true if type is primitive or String
  415. */
  416. private boolean isSimple(TypeMirror typeMirror, CompilationController controller){
  417. if ( typeMirror.getKind().isPrimitive() ){
  418. return true;
  419. }
  420. Element fieldTypeElement = controller.getTypes().asElement(typeMirror);
  421. TypeElement stringElement = controller.getElements().
  422. getTypeElement(String.class.getName());
  423. if ( stringElement != null && stringElement.equals( fieldTypeElement)){
  424. return true;
  425. }
  426. PackageElement pack = controller.getElements().getPackageOf(
  427. fieldTypeElement);
  428. if ( pack.getQualifiedName().contentEquals("java.lang")){ // NOI18N
  429. try {
  430. if ( controller.getTypes().unboxedType(typeMirror) != null ){
  431. return true;
  432. }
  433. }
  434. catch(IllegalArgumentException e){
  435. // just skip field
  436. }
  437. }
  438. return false;
  439. }
  440. private void overrideMethod(String url, String path, Boolean useId,
  441. HttpRequests request, StringBuilder builder ) throws IOException
  442. {
  443. if ( path == null ){
  444. builder.append("if(method=='"); // NOI18N
  445. builder.append(request.toString());
  446. builder.append("'){\n"); // NOI18N
  447. builder.append("return false;\n}\n"); // NOI18N
  448. }
  449. else {
  450. path = getUrl(path);
  451. StringBuilder newUrlSnippet = new StringBuilder();
  452. boolean isModified = false;
  453. if ( !url.equals(path) ){
  454. newUrlSnippet.append("options.url = '"); // NOI18N
  455. newUrlSnippet.append(path);
  456. isModified = true;
  457. }
  458. if (useId!= null && useId){
  459. if ( isModified ){
  460. if ( !path.endsWith("/")){
  461. newUrlSnippet.append('/');
  462. }
  463. newUrlSnippet.append("'+model.id;\n");
  464. }
  465. }
  466. else{
  467. if ( isModified ){
  468. newUrlSnippet.append("';\n");
  469. }
  470. else{
  471. newUrlSnippet.append("options.url = '"); // NOI18N
  472. newUrlSnippet.append(path);
  473. newUrlSnippet.append("';\n");
  474. }
  475. }
  476. if ( newUrlSnippet.length() == 0 ){
  477. return;
  478. }
  479. builder.append("if(method=='"); // NOI18N
  480. builder.append(request.toString());
  481. builder.append("'){\n"); // NOI18N
  482. builder.append(newUrlSnippet);
  483. builder.append("}\n"); // NOI18N
  484. }
  485. }
  486. private String addUrlPath( String path, String uri ) {
  487. if (uri.startsWith(SLASH)) {
  488. if (path.endsWith(SLASH)) {
  489. path = path + uri.substring(1);
  490. }
  491. else {
  492. path = path + uri;
  493. }
  494. }
  495. else {
  496. if (path.endsWith(SLASH)) {
  497. path = path + uri;
  498. }
  499. else {
  500. path = path + SLASH + uri;
  501. }
  502. }
  503. return path;
  504. }
  505. private StringBuilder myCommonModels;
  506. private RestServiceDescription myDescription;
  507. private Set<String> myEntities ;
  508. private JsUi myUi;
  509. private Set<ModelAttribute> myAttributes;
  510. private String myDisplayNameAlias;
  511. private String myModelName;
  512. private String myCollectionModelName;
  513. private ModelAttribute myIdAttribute;
  514. }