OSDN Git Service

i
[luz/luz.git] / lacoder / src / com / lavans / lacoder / model / Attribute.java
1 /* $Id: Attribute.java 451 2011-08-12 11:38:19Z dobashi $\r
2  * create: 2004/12/28\r
3  * (c)2004 Lavans Networks Inc. All Rights Reserved.\r
4  */\r
5 package com.lavans.lacoder.model;\r
6 \r
7 import com.lavans.lacoder.main.JavaType;\r
8 import com.lavans.luz2.commons.StringUtils;\r
9 \r
10 /**\r
11  * toGetParameter/toSetParameterにjspでやるべき事が混じってるような感じ。\r
12  * やはりmap.put()あたりはjspに持って行くべきか?\r
13  * @author dobashi\r
14  * @version 1.00\r
15  */\r
16 public class Attribute{\r
17 //      private static Log logger = LogFactory.getLog(Attribute.class);\r
18 \r
19         /** 親となるエンティティ */\r
20         private Entity entity = null;\r
21         /** 名前。意味区切り大文字 */\r
22         private String name = null;\r
23         /** タイトル */\r
24         private String title = null;\r
25         /** DB上の型 */\r
26         private String dbType = null;\r
27         /** javaの型 */\r
28         private String javaType = null;\r
29         /** 対応するクラス名 */\r
30         private String className = null;\r
31 //      private String classAttrName = null;\r
32         // list形式の場合\r
33         private boolean isList = false;\r
34         // 固定長リストの場合\r
35         private int listSize=0;\r
36 \r
37         /** 日付入力用のformat。 */\r
38         private String dateFormat = "yyyy/MM/dd";\r
39 \r
40         /** sequence flag */\r
41         private boolean isSequence = false;\r
42 \r
43         /** enum flag */\r
44         private boolean isEnum = false;\r
45 \r
46         /** primary key flag */\r
47         private boolean isPrimaryKey = false;\r
48 \r
49         // constraint 制約\r
50         /** nullable flag. default = false. */\r
51         private boolean isNullable = false;\r
52 \r
53         /** precision for BigDecimal */\r
54         private Integer precision = null;\r
55         /** scale for BigDecimal */\r
56         private Integer scale = null;\r
57 \r
58         // 最小値最大値。\r
59         // longで指定できる範囲ならvalidate()で判定処理を自動生成する。\r
60         // 数値なら大きさ、文字列なら長さを指定する。\r
61         /** min value */\r
62         private Long min = null;\r
63         /** max value */\r
64         private Long max = null;\r
65 \r
66         /**\r
67          * 初期化値を返す。主にStringの初期値を""にして新規登録時にjspにnullが表示されないようにするため。\r
68          * @return\r
69          */\r
70         public String getInitValue(){\r
71 //              logger.debug(arg0)\r
72                 String result = null;\r
73                 if(getJavaType().equals("int") ||\r
74                         getJavaType().equals("long") ||\r
75                         getJavaType().equals("double") ||\r
76                         getJavaType().equals("float") ){\r
77                         result = "0";\r
78                 }else if(getJavaType().equals("boolean")){\r
79                         result = "false";\r
80                 }else if(getName().equals("statusEnumId") ){\r
81                         // ステータスEnumのデフォルト値\r
82                         result = "\"on\"";\r
83                 }else if(getJavaType().equals("String")){\r
84                         result = "\"\"";\r
85                 }else if(!isEditable()){\r
86                         result = "null";\r
87                 }else if(getJavaType().equals("Date")){\r
88 //                      result = "new Date()";\r
89                         result = "null";\r
90                 }else{\r
91                         result = "null";\r
92                 }\r
93                 return result;\r
94         }\r
95 \r
96         /**\r
97          * jdbcでのset/getメソッドでの型名を返す。\r
98          * @return\r
99          */\r
100         public String getJdbcMethodName(){\r
101                 // Oracleでbyte[]の時はBytesじゃなくてBlobにしないといけないかも\r
102                 return JavaType.getJdbcMethodName(javaType);\r
103         }\r
104 \r
105         /**\r
106          * INSERT文でのvalue名を返す。\r
107          * @return\r
108          */\r
109         public String getInsertSql(){\r
110                 return  ", :"+name;\r
111         }\r
112 \r
113         /**\r
114          * UPDATE文でのフィールド名を返す。\r
115          * @return\r
116          */\r
117         public String getUpdateSql(){\r
118                 String result = null;\r
119 \r
120                 if(isPrimaryKey){\r
121                         // PKはWHERE句に含まれている\r
122                         result="";\r
123                 }else if(name.equals("insert_datetime")){\r
124                         // insert_dateは更新できない\r
125                         result = "";\r
126                 }else{\r
127                         result = ", "+ getColumnName()+"=:"+name;\r
128                 }\r
129                 return result;\r
130         }\r
131 \r
132         /**\r
133          * テーブルでのカラム名を返す。\r
134          * @return\r
135          */\r
136         public String getColumnName(){\r
137                 return StringUtils.toUnderscore(name).toUpperCase();\r
138         }\r
139 \r
140         /**\r
141          * パラメータ化する時の値を返す。Stringならそのまま。\r
142          * それ以外のクラスならgetId()を返す。プリミティブ型なら文字列に変換。\r
143          * @return\r
144          */\r
145         public String toGetParameter(){\r
146                 // insert_date/userはパラメータ化の必要なし。\r
147                 if(!isEditable()){\r
148                         return "";\r
149                 }\r
150 \r
151                 String result  = null;\r
152 //              if(!className.equals("")){\r
153 //                      if(entity.getTypeManager().getJavaType(type).equals("int")){\r
154 //                              result = "new Integer("+ getVarName() + ".getId())";\r
155 //                      }else{\r
156 //                              result = getVarName() + ".getId()";\r
157 //                      }\r
158 //              }else\r
159                 if(isList){\r
160                         // とりあえずリストはOutputパラメータだけなので無視\r
161                         return "";\r
162 //                      buf.append(getClassLastName() +".getInstance(((String[])map.get(prefix+"+ getConstName() +"))[0]);");\r
163                 }else if(getJavaType().equals("String")){\r
164                         result = getVarName();\r
165                 }else if(getJavaType().equals("Date")){\r
166                         // date型は値が入っていない場合NullPo抑制が必要\r
167 \r
168                         result = name + "==null?\"\":"+ getGetterName() +"Str()";\r
169                 }else if(getJavaType().equals("byte[]")){\r
170                         // Base64で埋められるけど、バイナリデータをformに入れて持ち回りたいことはないのでなにもしない。\r
171                         // でも後で入れるかもしれないからコメントで残しておく\r
172                         //result = "StringUtils.encodeBase64Url( + name + ")";\r
173                 }else{\r
174                         result = "String.valueOf("+ getVarName() +")";\r
175                 }\r
176                 result = "map.put(prefix+"+ getConstName() +", new String[]{"+ result +"});";\r
177 \r
178                 return result;\r
179         }\r
180 \r
181         /**\r
182          * mapからインスタンス変数へ代入。\r
183          * @return\r
184          */\r
185         public String toSetParameter(){\r
186                 // insert_date/userはパラメータ化の必要なし。\r
187                 if(!isEditable()){\r
188                         return "";\r
189                 }\r
190 \r
191                 StringBuffer buf = new StringBuffer();\r
192                 buf.append("if(map.get(prefix+"+ getConstName() +")!=null) "+ getVarName() +" = ");\r
193 \r
194                 if(isList){\r
195                         // とりあえずリストはOutputパラメータだけなので無視\r
196                         return "";\r
197 //                      buf.append(getClassLastName() +".getInstance(((String[])map.get(prefix+"+ getConstName() +")));");\r
198                 }else if(getJavaType().equals("int")){\r
199                         buf.append("Integer.parseInt(map.get(prefix+"+ getConstName() +")[0]);");\r
200                 }else if(getJavaType().equals("long")){\r
201                         buf.append("Long.parseLong(map.get(prefix+"+ getConstName() +")[0]);");\r
202                 }else if(getJavaType().equals("double")){\r
203                         buf.append("Double.parseDouble(map.get(prefix+"+ getConstName() +")[0]);");\r
204                 }else if(getJavaType().equals("boolean")){\r
205                         buf.append("Boolean.parseBoolean(map.get(prefix+"+ getConstName() +")[0]);");\r
206                 }else if(getJavaType().equals("Date")){\r
207                         buf.append(getGetterName() +"DateFormat().parse(map.get(prefix+"+ getConstName() +")[0]);");\r
208                 }else if(getJavaType().equals("BigDecimal")){\r
209                         buf.append("new BigDecimal(map.get(prefix+"+ getConstName() +")[0]);");\r
210                 }else if(getJavaType().equals("byte[]")){\r
211                         //buf.append("StringUtils.decodeBase64Url(map.get(prefix+"+ getConstName() +"));");\r
212                 }else{  // それ以外(String)\r
213                         buf.append("map.get(prefix+"+ getConstName() +")[0];");\r
214                 }\r
215 \r
216                 return "try{ "+ buf.toString() +"}catch(Exception e){}";\r
217         }\r
218 \r
219         /**\r
220          * パラメータ化する時の値を返す。Stringならそのまま。\r
221          * それ以外のクラスならgetId()を返す。プリミティブ型なら文字列に変換。\r
222          * @return\r
223          */\r
224         public String toGetAttributeInfo(){\r
225                 StringBuilder buf = new StringBuilder();\r
226                 buf.append("map.put("+ getConstName() +", ");\r
227 \r
228                 if(getJavaType().equals("int")){\r
229                         buf.append("Integer.class);");\r
230                 }else if(getJavaType().equals("long")){\r
231                         buf.append("Long.class);");\r
232                 }else if(getJavaType().equals("double")){\r
233                         buf.append("Double.class);");\r
234                 }else if(getJavaType().equals("boolean")){\r
235                         buf.append("Boolean.class);");\r
236                 }else if(getJavaType().equals("Date")){\r
237                         buf.append("java.sql.Date.class);");\r
238 //              }else if(getJavaType().equals("BigDecimal")){\r
239 //                      buf.append("BigDecimal.class;");\r
240 //              }else if(getJavaType().equals("byte[]")){\r
241 //                      buf.append("byte[].class;");\r
242                 }else{  // それ以外(String, BigDecimal, byt[])\r
243                         buf.append(getJavaType()+".class);");\r
244                 }\r
245 \r
246                 return buf.toString();\r
247         }\r
248 \r
249         /**\r
250          * パラメータ化する時の値を返す。Stringならそのまま。\r
251          * それ以外のクラスならgetId()を返す。プリミティブ型なら文字列に変換。\r
252          * @return\r
253          */\r
254         public String toGetAttributeMap(){\r
255                 String result  = null;\r
256                 if(isList){\r
257                         // リストは後で考える。Enumの配列とか?カンマ区切りStringの処理?\r
258                         return "";\r
259                 }\r
260                 result = "map.put("+ getConstName() +","+ getVarName() +");";\r
261 \r
262                 return result;\r
263         }\r
264 \r
265         public String toGetter(){\r
266                 StringBuffer buf = new StringBuffer();\r
267                 buf.append("    /**\n");\r
268                 buf.append("     * @return "+ getVarName() +"を戻します。\n");\r
269                 buf.append("     */\n");\r
270                 buf.append("    public "+ getJavaType() +" "+ getGetterName() +"(){\n");\r
271                 buf.append("            return "+ getVarName() +";\n");\r
272                 buf.append("    }\n");\r
273 \r
274                 if(hasClass()){\r
275                         buf.append("    /**\n");\r
276                         buf.append("     * @return "+ getClassLastName() +"を戻します。\n");\r
277                         buf.append("     */\n");\r
278                         buf.append("    public "+ getClassLastName() +" get"+ StringUtils.capitalize(getClassVarName()) +"(){\n");\r
279                         if(isEnum){\r
280                                 if(getJavaType().equals("boolean")){\r
281                                         buf.append("            return "+ getClassLastName() +".getInstance(Boolean.toString("+ getVarName() +"));\n");\r
282                                 }else if(getJavaType().equals("int")){\r
283                                         buf.append("            return "+ getClassLastName() +".getInstanceByInt("+ getVarName() +");\n");\r
284                                 }else{\r
285                                         buf.append("            return "+ getClassLastName() +".getInstance("+ getVarName() +");\n");\r
286                                 }\r
287                         }else{\r
288                                 buf.append("            return "+ getClassVarName() +";\n");\r
289                         }\r
290                         buf.append("    }\n");\r
291                         if(isEnum){\r
292                                 buf.append("    /**\n");\r
293                                 buf.append("     * @return "+ getVarName() +"Titleを戻します。\n");\r
294                                 buf.append("     */\n");\r
295                                 buf.append("    public String get"+ StringUtils.capitalize(getClassVarName()) +"Title(){\n");\r
296                                 buf.append("            if(get"+ StringUtils.capitalize(getClassVarName()) +"()==null) return \"\";\n");\r
297                                 buf.append("            return get"+ StringUtils.capitalize(getClassVarName()) +"().getTitle();\n");\r
298                                 buf.append("    }\n");\r
299                         }\r
300                         // enumの時はtitleも返す\r
301                 }else if(getJavaType().equals("Date")){\r
302                         buf.append("    /**\n");\r
303                         buf.append("     * @return "+ getVarName() +"の日付フォーマットを戻します。\n");\r
304                         buf.append("     */\n");\r
305                         buf.append("    public DateFormat "+ getGetterName() +"DateFormat(){\n");\r
306                         buf.append("            return new SimpleDateFormat(\""+ getDateFormat() +"\");\n");\r
307                         buf.append("    }\n");\r
308                         buf.append("    /**\n");\r
309                         buf.append("     * @return "+ getVarName() +"の日付文字列を戻します。\n");\r
310                         buf.append("     */\n");\r
311                         buf.append("    public String "+ getGetterName() +"Str(){\n");\r
312                         buf.append("            return "+ getGetterName() + "DateFormat().format("+ getVarName() +");\n");\r
313                         buf.append("    }\n");\r
314                 }\r
315 \r
316                 return buf.toString();\r
317         }\r
318 \r
319         /**\r
320          * setterの生成\r
321          * @return\r
322          */\r
323         public String toSetter(){\r
324                 StringBuffer buf = new StringBuffer();\r
325                 buf.append("    /**\n");\r
326                 buf.append("     *  "+ getVarName() +"を設定します。\n");\r
327                 buf.append("     */\n");\r
328                 buf.append("    public void "+ getSetterName() +"("+ getJavaType() +" "+ getVarName() +"){\n");\r
329                 buf.append("            this."+ getVarName() +"="+ getVarName() +";\n");\r
330                 buf.append("    }\n");\r
331 \r
332                 if(hasClass()){\r
333                         buf.append("    /**\n");\r
334                         buf.append("     * "+ getClassLastName() +"を設定します。\n");\r
335                         buf.append("     */\n");\r
336                         buf.append("    public void set"+ StringUtils.capitalize(getClassVarName()) +"("+ getClassLastName() +" "+ getClassVarName() +"){\n");\r
337                         if(isEnum){\r
338                                 if(getJavaType().equals("boolean")){\r
339                                         buf.append("            this."+ getVarName() +"=Boolean.parseBoolean("+ getClassVarName() +".getId());\n");\r
340                                 }else if(getJavaType().equals("int")){\r
341                                         buf.append("            this."+ getVarName() +"="+ getClassVarName() +".getInt();\n");\r
342                                 }else{\r
343                                         buf.append("            this."+ getVarName() +"="+ getClassVarName() +".getId();\n");\r
344                                 }\r
345                         }else{\r
346                                 buf.append("            this."+ getClassVarName() +"="+ getClassVarName() +";\n");\r
347                         }\r
348                         buf.append("    }\n");\r
349                 }else if(getJavaType().equals("Date")){\r
350                         buf.append("    /**\n");\r
351                         buf.append("     * "+ getVarName() +"の日付文字列を設定します。\n");\r
352                         buf.append("     */\n");\r
353                         buf.append("    public void "+ getSetterName() +"Str(String "+ getVarName() +"Str) throws ParseException{\n");\r
354                         buf.append("            this."+ getVarName() +"="+ getGetterName() + "DateFormat().parse("+ getVarName() +"Str);\n");\r
355                         buf.append("    }\n");\r
356                 }\r
357 \r
358                 return buf.toString();\r
359         }\r
360 \r
361         /**\r
362          *  insert_date/user等は入力不可。\r
363          * @return\r
364          */\r
365         public boolean isEditable(){\r
366                 // insert_date/userはパラメータ化の必要なし。\r
367                 if(name.equals("insert_datetime") || name.equals("update_datetime") ){\r
368                         return false;\r
369                 }\r
370                 return true;\r
371         }\r
372         /**\r
373          * @return entity を戻します。\r
374          */\r
375         public Entity getEntity() {\r
376                 return entity;\r
377         }\r
378         /**\r
379          * @param entity entity を設定。\r
380          */\r
381         public void setEntity(Entity entity) {\r
382                 this.entity = entity;\r
383         }\r
384 \r
385 //      /**\r
386 //       * @return classAttrName を戻します。\r
387 //       */\r
388 //      public String getClassAttrName() {\r
389 //              return classAttrName;\r
390 //      }\r
391 //      /**\r
392 //       * @param classAttrName classAttrName を設定。\r
393 //       */\r
394 //      public void setClassAttrName(String attrName) {\r
395 //              this.classAttrName = attrName;\r
396 //      }\r
397         /**\r
398          * @return className を戻します。\r
399          */\r
400         public String getClassName() {\r
401                 // クラス名が.で始まる場合はドメインパスまでを省略している\r
402                 if((className!=null) && className.startsWith(".")){\r
403                         className = entity.getParentPackage().getDomainPath()+className;\r
404                 }\r
405                 return className;\r
406         }\r
407         /**\r
408          * @return className を戻します。\r
409          */\r
410         public String getClassLastName() {\r
411                 String names[] = className.split("\\.");\r
412                 return names[names.length-1];\r
413         }\r
414         /**\r
415          * @param className className を設定。\r
416          */\r
417         public void setClassName(String className) {\r
418                 this.className = className;\r
419         }\r
420         /**\r
421          * @return name を戻します。\r
422          */\r
423         public String getName() {\r
424                 return name;\r
425         }\r
426         public String getConstName() {\r
427                 return StringUtils.toUnderscore(name).toUpperCase();\r
428         }\r
429         /**\r
430          * Entity内での変数名を返す。\r
431          * classを持っている場合、末尾がIdで終わらなければIdをつける。\r
432          * @return\r
433          */\r
434         public String getVarName() {\r
435                 if(!getClassName().equals("") && !name.endsWith("Id")){\r
436                         return name+"Id";\r
437                 }\r
438                 return name;\r
439         }\r
440         /**\r
441          * 最初を大文字にした名前を返す。\r
442          * @return\r
443          */\r
444         public String getCapitalizeName() {\r
445                 return StringUtils.capitalize(getVarName());\r
446         }\r
447 \r
448         /**\r
449          * Entity内でのクラス型の変数名を返す。\r
450          * 変数名が必ずIdで終わっているので、最後の2文字を削除した物をクラス型の変数名とする。\r
451          * @return\r
452          */\r
453         public String getClassVarName() {\r
454                 String classVarName = getVarName();\r
455                 classVarName = classVarName.substring(0, classVarName.length()-2);\r
456                 return classVarName;\r
457         }\r
458 \r
459         /**\r
460          * getterのメソッド名。customerId -> getCustomerId\r
461          * booleanでis/can/hasの場合はgetIsBoolとならずにisBoolだけとしたいところだけど\r
462          * struts2のActionの自動設定で失敗するのでgetIsBoolとする。\r
463          * @return\r
464          */\r
465         public String getGetterName(){\r
466 //              if(getJavaType().equals("boolean")){\r
467 //                      if(name.startsWith("is") || name.startsWith("can") || name.startsWith("has")){\r
468 //                              return name;\r
469 //                      }\r
470 //              }\r
471 \r
472                 return "get" + StringUtils.capitalize(getVarName());\r
473         }\r
474 \r
475         /**\r
476          * setterのメソッド名。customerId -> setCustomerId\r
477          * booleanでis/can/hasの場合はgetIsBoolとならずにisBoolだけとする\r
478          * @return\r
479          */\r
480         public String getSetterName(){\r
481                 return "set" + StringUtils.capitalize(getVarName());\r
482 \r
483         }\r
484         /**\r
485          * 変数名を頭大文字にして返す。getXX(),setXX()等で使用。\r
486          * @return\r
487          */\r
488 //      public String getVarNameUpperFirst(){\r
489 //              return StringUtils.upperFirst(name);\r
490 //      }\r
491 \r
492         /**\r
493          * @param name name を設定。\r
494          */\r
495         public void setName(String name) {\r
496                 this.name = name;\r
497         }\r
498 \r
499         /**\r
500          * @return isSequence を戻します。\r
501          */\r
502         public boolean isSequence() {\r
503                 return isSequence;\r
504         }\r
505         /**\r
506          * @param isSequence isSequence を設定。\r
507          */\r
508         public void setSequence(boolean isSequence) {\r
509                 this.isSequence = isSequence;\r
510         }\r
511         /**\r
512          * @return title を戻します。\r
513          */\r
514         public String getTitle() {\r
515                 return title;\r
516         }\r
517         /**\r
518          * @param title title を設定。\r
519          */\r
520         public void setTitle(String title) {\r
521                 this.title = title;\r
522         }\r
523         /**\r
524          * @return dateFormat を戻します。\r
525          */\r
526         public String getDateFormat() {\r
527                 return dateFormat;\r
528         }\r
529         /**\r
530          * @param dateFormat dateFormat を設定。\r
531          */\r
532         public void setDateFormat(String format) {\r
533                 this.dateFormat = format;\r
534         }\r
535         /**\r
536          * @return isEnum を戻します。\r
537          */\r
538         public boolean isEnum() {\r
539                 return isEnum;\r
540         }\r
541         /**\r
542          * @param isEnum isEnum を設定。\r
543          */\r
544         public void setEnum(boolean isEnum) {\r
545                 this.isEnum = isEnum;\r
546         }\r
547 \r
548         public boolean hasClass() {\r
549                 return !getClassName().equals("");\r
550         }\r
551 \r
552         /**\r
553          * EntityクラスにおけるJavaの型。\r
554          * @return\r
555          */\r
556         public String getJavaType(){\r
557                 return javaType;\r
558         }\r
559 \r
560         /**\r
561          * @param javaType javaType を設定。\r
562          */\r
563         public void setJavaType(String javaType) {\r
564                 this.javaType = javaType;\r
565         }\r
566         /**\r
567          * @return isList を戻します。\r
568          */\r
569         public boolean isList() {\r
570                 return isList;\r
571         }\r
572         /**\r
573          * @param isList isList を設定。\r
574          */\r
575         public void setList(boolean isList) {\r
576                 this.isList = isList;\r
577         }\r
578         /**\r
579          * @return listSize を戻します。\r
580          */\r
581         public int getListSize() {\r
582                 return listSize;\r
583         }\r
584         /**\r
585          * @param listSize listSize を設定。\r
586          */\r
587         public void setListSize(int listSize) {\r
588                 this.listSize = listSize;\r
589         }\r
590         /**\r
591          * @return isPrimaryKey を戻します。\r
592          */\r
593         public boolean isPrimaryKey() {\r
594                 return isPrimaryKey;\r
595         }\r
596         /**\r
597          * @param isPrimaryKey isPrimaryKey を設定。\r
598          */\r
599         public void setPrimaryKey(boolean isPrimaryKey) {\r
600                 this.isPrimaryKey = isPrimaryKey;\r
601         }\r
602 \r
603         public String getDbType() {\r
604                 // xmlで指定済みならそれを返す\r
605                 if(dbType!=null){\r
606                         return dbType;\r
607                 }\r
608                 // 未指定ならTypeManagerから取得。\r
609                 return entity.getTypeManager().getDbType(this);\r
610         }\r
611 \r
612         public void setDbType(String dbType) {\r
613                 this.dbType = dbType;\r
614         }\r
615 \r
616         public boolean isNullable() {\r
617                 return isNullable;\r
618         }\r
619 \r
620         public void setNullable(boolean isNullable) {\r
621                 this.isNullable = isNullable;\r
622         }\r
623 \r
624         public Integer getPrecision() {\r
625                 return precision;\r
626         }\r
627 \r
628         public void setPrecision(Integer precision) {\r
629                 this.precision = precision;\r
630         }\r
631 \r
632         public Integer getScale() {\r
633                 return scale;\r
634         }\r
635 \r
636         public void setScale(Integer scale) {\r
637                 this.scale = scale;\r
638         }\r
639 \r
640         public Long getMin() {\r
641                 return min;\r
642         }\r
643 \r
644         public void setMin(Long min) {\r
645                 this.min = min;\r
646         }\r
647 \r
648         public Long getMax() {\r
649                 return max;\r
650         }\r
651 \r
652         public void setMax(Long max) {\r
653                 this.max = max;\r
654         }\r
655 }\r