001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    import java.util.*;
011    import java.util.zip.*;
012    import java.io.*;
013    
014    aspect SpecialClasses {
015      syn lazy TypeDecl Program.typeObject() = lookupType("java.lang", "Object");
016      syn lazy TypeDecl Program.typeCloneable() = lookupType("java.lang", "Cloneable");
017      syn lazy TypeDecl Program.typeSerializable() = lookupType("java.io", "Serializable");
018      eq Program.getChild().typeObject() = typeObject();
019      eq Program.getChild().typeCloneable() = typeCloneable();
020      eq Program.getChild().typeSerializable() = typeSerializable();
021     
022      syn lazy TypeDecl Program.typeBoolean() = lookupType(PRIMITIVE_PACKAGE_NAME, "boolean");
023      syn lazy TypeDecl Program.typeByte() = lookupType(PRIMITIVE_PACKAGE_NAME , "byte");
024      syn lazy TypeDecl Program.typeShort() = lookupType(PRIMITIVE_PACKAGE_NAME , "short");
025      syn lazy TypeDecl Program.typeChar() = lookupType(PRIMITIVE_PACKAGE_NAME , "char");
026      syn lazy TypeDecl Program.typeInt() = lookupType(PRIMITIVE_PACKAGE_NAME , "int");
027      syn lazy TypeDecl Program.typeLong() = lookupType(PRIMITIVE_PACKAGE_NAME , "long");
028      syn lazy TypeDecl Program.typeFloat() = lookupType(PRIMITIVE_PACKAGE_NAME , "float");
029      syn lazy TypeDecl Program.typeDouble() = lookupType(PRIMITIVE_PACKAGE_NAME , "double");
030      syn lazy TypeDecl Program.typeString() = lookupType("java.lang", "String");
031      eq Program.getChild().typeBoolean() = typeBoolean();
032      eq Program.getChild().typeByte() = typeByte();
033      eq Program.getChild().typeShort() = typeShort();
034      eq Program.getChild().typeChar() = typeChar();
035      eq Program.getChild().typeInt() = typeInt();
036      eq Program.getChild().typeLong() = typeLong();
037      eq Program.getChild().typeFloat() = typeFloat();
038      eq Program.getChild().typeDouble() = typeDouble();
039      eq Program.getChild().typeString() = typeString();
040    
041      syn lazy TypeDecl Program.typeVoid() = lookupType(PRIMITIVE_PACKAGE_NAME, "void");
042      eq Program.getChild().typeVoid() = typeVoid();
043      syn lazy TypeDecl Program.typeNull() = lookupType(PRIMITIVE_PACKAGE_NAME, "null");
044      eq Program.getChild().typeNull() = typeNull();
045    
046      syn lazy TypeDecl Program.unknownType() = lookupType(PRIMITIVE_PACKAGE_NAME, "Unknown");
047      eq Program.getChild().unknownType() = unknownType();
048      
049      inh TypeDecl Expr.typeBoolean();
050      inh TypeDecl Expr.typeByte();
051      inh TypeDecl Expr.typeShort();
052      inh TypeDecl Expr.typeChar();
053      inh TypeDecl Expr.typeInt();
054      inh TypeDecl Expr.typeLong();
055      inh TypeDecl Expr.typeFloat();
056      inh TypeDecl Expr.typeDouble();
057      inh TypeDecl Expr.typeString();
058      inh TypeDecl Expr.typeVoid();
059      inh TypeDecl Expr.typeNull();
060    
061      inh lazy TypeDecl SwitchStmt.typeInt();
062      inh TypeDecl TypeDecl.typeInt();
063      inh lazy TypeDecl SwitchStmt.typeLong();
064      
065      inh lazy TypeDecl TypeDecl.typeObject();
066    
067      inh lazy TypeDecl ThrowStmt.typeThrowable();
068      inh lazy TypeDecl CatchClause.typeThrowable();
069      
070      inh lazy TypeDecl ThrowStmt.typeNull();
071    
072      inh TypeDecl Expr.unknownType();
073    
074    }
075    
076    aspect LookupFullyQualifiedTypes {
077      syn lazy boolean Program.hasPackage(String packageName) {
078        return isPackage(packageName);
079      }
080      // The scope of a declaration of an observable top level package is all observable compilation units
081      eq Program.getChild().hasPackage(String packageName) = hasPackage(packageName);
082      eq AbstractDot.getRight().hasPackage(String packageName) = getLeft().hasQualifiedPackage(packageName);
083      syn boolean Expr.hasQualifiedPackage(String packageName) = false;
084      eq PackageAccess.hasQualifiedPackage(String packageName) =
085        hasPackage(packageName() + "." + packageName);
086      inh boolean Expr.hasPackage(String packageName);
087      eq MethodAccess.getArg().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
088      eq ConstructorAccess.getArg().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
089      eq SuperConstructorAccess.getArg().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
090      eq ArrayAccess.getExpr().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
091      eq ArrayTypeWithSizeAccess.getExpr().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
092      eq ClassInstanceExpr.getArg().hasPackage(String packageName) = unqualifiedScope().hasPackage(packageName);
093    
094      
095      inh TypeDecl Expr.lookupType(String packageName, String typeName);
096      inh TypeDecl Stmt.lookupType(String packageName, String typeName);
097      inh TypeDecl BodyDecl.lookupType(String packageName, String typeName);
098      inh TypeDecl TypeDecl.lookupType(String packageName, String typeName);
099      inh TypeDecl CompilationUnit.lookupType(String packageName, String typeName);
100    
101      public int Program.classFileReadTime;
102    
103    /* ES: replacing this with an implementation that places library compilation units in a parameterized nta
104      eq Program.getChild().lookupType(String packageName, String typeName) = lookupType(packageName, typeName);
105      
106      syn lazy TypeDecl Program.lookupType(String packageName, String typeName) {
107        addPrimitiveTypes();
108        String fullName = packageName.equals("") ? typeName : packageName + "." + typeName;
109        for(int i = 0; i < getNumCompilationUnit(); i++) {
110          for(int j = 0; j < getCompilationUnit(i).getNumTypeDecl(); j++) {
111            TypeDecl type = getCompilationUnit(i).getTypeDecl(j);
112            if(type.fullName().equals(fullName)) {
113              return type;
114            }
115          }
116        }
117        
118        CompilationUnit u = getCompilationUnit(fullName);
119        if(u != null) {
120          addCompilationUnit(u);
121          getCompilationUnit(getNumCompilationUnit()-1);
122          for(int j = 0; j < u.getNumTypeDecl(); j++) {
123            if(u.getTypeDecl(j).name().equals(typeName)) {
124              return u.getTypeDecl(j);
125            }
126          }
127          //throw new Error("No type named " + typeName + " in file " + fullName + ", " + u.pathName() + ", " + u.relativeName());
128        }
129        return null;
130      }
131    */
132    
133      /*
134       * REMOVED by Jesper on 2012-06-29
135       * The from-source compilation units should always be searched.
136       * Some library compilation units may use from-source compilation units,
137       * and in this case we do not want to add a duplicate compilation unit
138       * in the library compilation unit list.
139       *
140       * Old code:
141       *
142       * // Type requests from the library stays in the library
143       * eq Program.getChild().lookupType(String packageName, String typeName) = lookupLibType(packageName, typeName);
144       * // Type requests from the source checks the source and then the library
145       * eq Program.getCompilationUnit(int i).lookupType(String packageName, String typeName) = lookupType(packageName, typeName);
146       */
147    
148      /**
149       * Defers the lookup to the synthesized attribute.
150       */
151      eq Program.getChild().lookupType(String packageName, String typeName) = lookupType(packageName, typeName);
152    
153      /**
154       * Checks from-source compilation units for the given type.
155       * If no matching compilation unit is found the library compliation units
156       * will be searched.
157       */
158      syn lazy TypeDecl Program.lookupType(String packageName, String typeName) {
159        String fullName = packageName.equals("") ? typeName : packageName + "." + typeName;
160        // Check for type in source
161        for(int i = 0; i < getNumCompilationUnit(); i++) {
162          for(int j = 0; j < getCompilationUnit(i).getNumTypeDecl(); j++) {
163            TypeDecl type = getCompilationUnit(i).getTypeDecl(j);
164            if(type.fullName().equals(fullName)) {
165              return type;
166            }
167          }
168        }
169        // Check for type in library
170        return lookupLibType(packageName, typeName);
171      }
172      /**
173       * Lookup types in the library
174       */
175      syn lazy TypeDecl Program.lookupLibType(String packageName, String typeName) {
176        String fullName = packageName.equals("") ? typeName : packageName + "." + typeName;
177        // Check the primitive types
178        if (packageName.equals(PRIMITIVE_PACKAGE_NAME)) {
179          PrimitiveCompilationUnit unit = getPrimitiveCompilationUnit();
180          if (typeName.equals("boolean")) return unit.typeBoolean();
181          if (typeName.equals("byte")) return unit.typeByte();
182          if (typeName.equals("short")) return unit.typeShort();
183          if (typeName.equals("char")) return unit.typeChar();
184          if (typeName.equals("int")) return unit.typeInt();
185          if (typeName.equals("long")) return unit.typeLong();
186          if (typeName.equals("float")) return unit.typeFloat();
187          if (typeName.equals("double")) return unit.typeDouble();
188          if (typeName.equals("null")) return unit.typeNull();
189          if (typeName.equals("void")) return unit.typeVoid();
190          if (typeName.equals("Unknown")) return unit.unknownType(); // Is this needed?
191        } 
192        // Check the library:
193        //  A type may not be in the library but an NTA cannot map to null.
194        //  We need to do some double work to step around this.
195        //  We check the classpath directly (the same thing the library NTA does)
196        //  to prevent that we call the nta for a name that gives null back
197        //else if (getCompilationUnit(fullName) != null) { 
198        
199        // Found a library unit, check it for type
200        CompilationUnit libUnit = getLibCompilationUnit(fullName);
201        if (libUnit != null) {
202          for(int j = 0; j < libUnit.getNumTypeDecl(); j++) {
203            TypeDecl type = libUnit.getTypeDecl(j);
204            if(type.fullName().equals(fullName)) {
205              return type;
206            }
207          }
208        }
209        // No type found in the library
210        return null;
211      }
212      // Store library units (including primitive unit) in a parameterized nta
213      syn nta CompilationUnit Program.getLibCompilationUnit(String fullName) {
214        return getCompilationUnit(fullName);
215      }
216    
217    
218    }
219    
220    aspect TypeScopePropagation {
221      inh TypeDecl Access.unknownType();
222    
223      syn lazy SimpleSet TypeAccess.decls() {
224        if(packageName().equals(""))
225          return lookupType(name());
226        else {
227          TypeDecl typeDecl = lookupType(packageName(), name());
228          if(typeDecl != null)
229            return SimpleSet.emptySet.add(typeDecl);
230          return SimpleSet.emptySet;
231        }
232      }
233    
234      syn lazy SimpleSet PrimitiveTypeAccess.decls() = lookupType(PRIMITIVE_PACKAGE_NAME, name());
235      syn lazy String PrimitiveTypeAccess.getPackage() = PRIMITIVE_PACKAGE_NAME;
236      syn lazy String PrimitiveTypeAccess.getID() = getName();
237      
238      syn lazy TypeDecl TypeAccess.decl() {
239        SimpleSet decls = decls();
240        if(decls.size() == 1) {
241          return (TypeDecl)decls.iterator().next();
242        }
243        return unknownType();
244      }
245      
246      syn lazy TypeDecl ArrayTypeAccess.decl() = getAccess().type().arrayType();
247      
248      syn SimpleSet ThisAccess.decls() = SimpleSet.emptySet;
249      syn SimpleSet SuperAccess.decls() = SimpleSet.emptySet;
250      syn lazy TypeDecl ThisAccess.decl() = isQualified() ? qualifier().type() : hostType();
251      syn lazy TypeDecl SuperAccess.decl() = isQualified() ? qualifier().type() : hostType();
252      
253      eq MethodAccess.getArg().lookupType(String name) = unqualifiedScope().lookupType(name);
254      eq ConstructorAccess.getArg().lookupType(String name) = unqualifiedScope().lookupType(name);
255      eq ArrayAccess.getExpr().lookupType(String name) = unqualifiedScope().lookupType(name);
256      eq ArrayTypeWithSizeAccess.getExpr().lookupType(String name) = unqualifiedScope().lookupType(name);
257      eq ClassInstanceExpr.getArg().lookupType(String name) = unqualifiedScope().lookupType(name);
258    
259      inh lazy SimpleSet CompilationUnit.lookupType(String name);
260      inh lazy SimpleSet TypeDecl.lookupType(String name);
261      inh SimpleSet BodyDecl.lookupType(String name);
262      inh SimpleSet Stmt.lookupType(String name);
263      inh lazy SimpleSet Block.lookupType(String name);
264      inh SimpleSet Expr.lookupType(String name);
265    
266      eq Program.getChild().lookupType(String name) = SimpleSet.emptySet;
267      
268      // The scope of a type import declaration is all the class and interface type declarations in
269      // the compilation unit in which the import declaration appears.
270      eq CompilationUnit.getChild().lookupType(String name) {
271        // locally declared types in compilation unit
272        SimpleSet set = localLookupType(name);
273        if(!set.isEmpty()) return set;
274    
275        // imported types
276        set = importedTypes(name);
277        if(!set.isEmpty()) return set;
278    
279        // types in the same package
280        TypeDecl result = lookupType(packageName(), name);
281        if(result != null && result.accessibleFromPackage(packageName())) 
282          return SimpleSet.emptySet.add(result);
283        
284        // types imported on demand
285        set = importedTypesOnDemand(name);
286        if(!set.isEmpty()) return set;
287        
288        // include primitive types
289        result = lookupType(PRIMITIVE_PACKAGE_NAME, name);
290        if(result != null) return SimpleSet.emptySet.add(result);
291        
292        // 7.5.5 Automatic Imports
293        result = lookupType("java.lang", name);
294        if(result != null && result.accessibleFromPackage(packageName()))
295          return SimpleSet.emptySet.add(result);
296        return lookupType(name);
297      }
298        
299      syn SimpleSet CompilationUnit.localLookupType(String name) {
300        for(int i = 0; i < getNumTypeDecl(); i++)
301          if(getTypeDecl(i).name().equals(name))
302            return SimpleSet.emptySet.add(getTypeDecl(i));
303        return SimpleSet.emptySet;
304      }
305      
306      syn SimpleSet CompilationUnit.importedTypes(String name) {
307        SimpleSet set = SimpleSet.emptySet;
308        for(int i = 0; i < getNumImportDecl(); i++)
309          if(!getImportDecl(i).isOnDemand())
310            for(Iterator iter = getImportDecl(i).importedTypes(name).iterator(); iter.hasNext(); )
311              set = set.add(iter.next());
312        return set;
313      }
314      syn SimpleSet CompilationUnit.importedTypesOnDemand(String name) {
315        SimpleSet set = SimpleSet.emptySet;
316        for(int i = 0; i < getNumImportDecl(); i++)
317          if(getImportDecl(i).isOnDemand())
318            for(Iterator iter = getImportDecl(i).importedTypes(name).iterator(); iter.hasNext(); )
319              set = set.add(iter.next());
320        return set;
321      }
322      syn lazy SimpleSet ImportDecl.importedTypes(String name) = SimpleSet.emptySet;
323      eq SingleTypeImportDecl.importedTypes(String name) {
324        SimpleSet set = SimpleSet.emptySet;
325        if(getAccess().type().name().equals(name))
326          set = set.add(getAccess().type());
327        return set;
328      }
329      eq TypeImportOnDemandDecl.importedTypes(String name) {
330        SimpleSet set = SimpleSet.emptySet;
331        if(getAccess() instanceof PackageAccess) {
332          String packageName = ((PackageAccess)getAccess()).getPackage();
333          TypeDecl typeDecl = lookupType(packageName, name);
334          if(typeDecl != null && typeDecl.accessibleFromPackage(packageName()) &&
335             typeDecl.typeName().equals(packageName + "." + name)) // canonical names match
336            set = set.add(typeDecl);
337        }
338        else {
339          for(Iterator iter = getAccess().type().memberTypes(name).iterator(); iter.hasNext(); ) {
340            TypeDecl decl = (TypeDecl)iter.next();
341            if(decl.accessibleFromPackage(packageName()) &&
342               decl.typeName().equals(getAccess().typeName() + "." + name)) // canonical names match
343              set = set.add(decl);
344          }
345        }
346        return set;
347      }
348      inh TypeDecl TypeImportOnDemandDecl.lookupType(String packageName, String typeName);
349      inh String ImportDecl.packageName();
350    
351      syn boolean ImportDecl.isOnDemand() = false;
352      eq TypeImportOnDemandDecl.isOnDemand() = true;
353    
354      // imports are not themselves affected by imports
355      eq CompilationUnit.getImportDecl().lookupType(String name) =
356        lookupType(name);
357    
358      eq TypeDecl.getBodyDecl().lookupType(String name) {
359        SimpleSet c = memberTypes(name);
360        if(!c.isEmpty()) 
361          return c;
362        if(name().equals(name))
363          return SimpleSet.emptySet.add(this);
364    
365        c = lookupType(name);
366        // 8.5.2
367        if(isClassDecl() && isStatic() && !isTopLevelType()) {
368          SimpleSet newSet = SimpleSet.emptySet;
369          for(Iterator iter = c.iterator(); iter.hasNext(); ) {
370            TypeDecl d = (TypeDecl)iter.next();
371            //if(d.isStatic() || d.isTopLevelType() || this.instanceOf(d.enclosingType())) {
372              newSet = newSet.add(d);
373            //}
374          }
375          c = newSet;
376        }
377        return c;
378      }
379    
380      eq Block.getStmt(int index).lookupType(String name) {
381        SimpleSet c = SimpleSet.emptySet;
382        for(int i = index; i >= 0 && !(getStmt(i) instanceof Case); i--) {
383          if(getStmt(i) instanceof LocalClassDeclStmt) {
384            TypeDecl t = ((LocalClassDeclStmt)getStmt(i)).getClassDecl();
385            if(t.name().equals(name)) {
386              c = c.add(t);
387            }
388          }
389        }
390        if(!c.isEmpty())
391          return c;
392        return lookupType(name);
393      }
394    
395      eq ClassInstanceExpr.getAccess().lookupType(String name) {
396        SimpleSet c = lookupType(name);
397        if(c.size() == 1) {
398          if(isQualified())
399            c = keepInnerClasses(c);
400        }
401        return c;
402      }
403      
404      eq ClassInstanceExpr.getTypeDecl().lookupType(String name) {
405        SimpleSet c = localLookupType(name);
406        if(!c.isEmpty())
407          return c;
408        c = lookupType(name);
409        if(!c.isEmpty())
410          return c;
411        return unqualifiedScope().lookupType(name);
412      }
413    
414      public SimpleSet ClassInstanceExpr.keepInnerClasses(SimpleSet c) {
415        SimpleSet newSet = SimpleSet.emptySet;
416        for(Iterator iter = c.iterator(); iter.hasNext(); ) {
417          TypeDecl t = (TypeDecl)iter.next();
418          if(t.isInnerType() && t.isClassDecl()) {
419            newSet = newSet.add(c);
420          }
421        }
422        return newSet;
423      }
424      
425      eq ParseName.qualifiedLookupType(String name) = SimpleSet.emptySet;
426      eq PackageOrTypeAccess.qualifiedLookupType(String name) = SimpleSet.emptySet;
427      eq AmbiguousAccess.qualifiedLookupType(String name) = SimpleSet.emptySet;
428    
429      eq AbstractDot.getRight().lookupType(String name) = getLeft().qualifiedLookupType(name);
430      syn SimpleSet Expr.qualifiedLookupType(String name) =
431        keepAccessibleTypes(type().memberTypes(name));
432    
433      eq ClassInstanceExpr.qualifiedLookupType(String name) {
434        SimpleSet c = keepAccessibleTypes(type().memberTypes(name));
435        if(!c.isEmpty())
436          return c;
437        if(type().name().equals(name))
438          return SimpleSet.emptySet.add(type());
439        return SimpleSet.emptySet;
440      }
441    
442      eq PackageAccess.qualifiedLookupType(String name) {
443        SimpleSet c = SimpleSet.emptySet;
444        TypeDecl typeDecl = lookupType(packageName(), name);
445        if(nextAccess() instanceof ClassInstanceExpr) {
446          if(typeDecl != null && typeDecl.accessibleFrom(hostType()))
447            c = c.add(typeDecl);
448          return c;
449        }
450        else {
451          if(typeDecl != null) {
452            if(hostType() != null && typeDecl.accessibleFrom(hostType()))
453              c = c.add(typeDecl);
454            else if(hostType() == null && typeDecl.accessibleFromPackage(hostPackage()))
455              c = c.add(typeDecl);
456          }
457          return c;
458        }
459      }
460        
461      public SimpleSet Expr.keepAccessibleTypes(SimpleSet oldSet) {
462        SimpleSet newSet = SimpleSet.emptySet;
463        TypeDecl hostType = hostType();
464        for(Iterator iter = oldSet.iterator(); iter.hasNext(); ) {
465          TypeDecl t = (TypeDecl)iter.next();
466          if((hostType != null && t.accessibleFrom(hostType)) || (hostType == null && t.accessibleFromPackage(hostPackage())))
467            newSet = newSet.add(t);
468        }
469        return newSet;
470      }
471    
472      syn lazy SimpleSet ClassInstanceExpr.localLookupType(String name) {
473        if(hasTypeDecl() && getTypeDecl().name().equals(name))
474          return SimpleSet.emptySet.add(getTypeDecl());
475        return SimpleSet.emptySet;
476      }
477    
478      syn boolean TypeDecl.hasType(String name) = !memberTypes(name).isEmpty();
479      syn boolean BodyDecl.declaresType(String name) = false;
480      eq MemberTypeDecl.declaresType(String name) = typeDecl().name().equals(name);
481      syn TypeDecl BodyDecl.type(String name) = null;
482      eq MemberTypeDecl.type(String name) = declaresType(name) ? typeDecl() : null;
483    
484      syn TypeDecl MemberTypeDecl.typeDecl();
485      eq MemberClassDecl.typeDecl() = getClassDecl();
486      eq MemberInterfaceDecl.typeDecl() = getInterfaceDecl();
487      
488    
489      syn lazy SimpleSet TypeDecl.localTypeDecls(String name) {
490        SimpleSet set = SimpleSet.emptySet;
491        for(int i = 0; i < getNumBodyDecl(); i++)
492          if(getBodyDecl(i).declaresType(name))
493            set = set.add(getBodyDecl(i).type(name));
494        return set;
495      }
496    
497      syn lazy SimpleSet TypeDecl.memberTypes(String name) = SimpleSet.emptySet;
498      eq ClassDecl.memberTypes(String name) {
499        SimpleSet set = localTypeDecls(name);
500        if(!set.isEmpty()) return set;
501        for(Iterator outerIter = interfacesIterator(); outerIter.hasNext(); ) {
502          TypeDecl type = (TypeDecl)outerIter.next();
503          for(Iterator iter = type.memberTypes(name).iterator(); iter.hasNext(); ) {
504            TypeDecl decl = (TypeDecl)iter.next();
505            if(!decl.isPrivate() && decl.accessibleFrom(this))
506              set = set.add(decl);
507          }
508        }
509        if(hasSuperclass()) {
510          for(Iterator iter = superclass().memberTypes(name).iterator(); iter.hasNext(); ) {
511            TypeDecl decl = (TypeDecl)iter.next();
512            if(!decl.isPrivate() && decl.accessibleFrom(this)) {
513              set = set.add(decl);
514            }
515          }
516        }
517        return set;
518      }
519    
520      eq InterfaceDecl.memberTypes(String name) {
521        SimpleSet set = localTypeDecls(name);
522        if(!set.isEmpty()) return set;
523        for(Iterator outerIter = superinterfacesIterator(); outerIter.hasNext(); ) {
524          TypeDecl typeDecl = (TypeDecl)outerIter.next();
525          for(Iterator iter = typeDecl.memberTypes(name).iterator(); iter.hasNext(); ) {
526            TypeDecl decl = (TypeDecl)iter.next();
527            if(!decl.isPrivate())
528              set = set.add(decl);
529          }
530        }
531        return set;
532      }
533    }