001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.cp;
018
019import static java.util.stream.Collectors.*;
020import static java.util.stream.Collectors.toList;
021import static org.apache.juneau.collections.JsonMap.*;
022import static org.apache.juneau.common.utils.StringUtils.*;
023import static org.apache.juneau.common.utils.Utils.*;
024import static org.apache.juneau.internal.CollectionUtils.map;
025
026import java.lang.annotation.*;
027import java.util.*;
028import java.util.concurrent.*;
029import java.util.function.*;
030import java.util.stream.*;
031
032import org.apache.juneau.*;
033import org.apache.juneau.collections.*;
034import org.apache.juneau.common.utils.*;
035import org.apache.juneau.internal.*;
036import org.apache.juneau.marshaller.*;
037import org.apache.juneau.reflect.*;
038
039/**
040 * Java bean store.
041 *
042 * <p>
043 * A simple storage database for beans keyed by type and name.
044 * Used to retrieve and instantiate beans using an injection-like API.
045 * It's similar in concept to the injection framework of Spring but greatly simplified in function and not intended to implement a full-fledged injection framework.
046 *
047 * <p>
048 * Beans can be stored with or without names.  Named beans are typically resolved using
049 * the <ja>@Named</ja> or <ja>@Qualified</ja> annotations on constructor or method parameters.
050 *
051 * <p>
052 * Beans are added through the following methods:
053 * <ul class='javatreec'>
054 *    <li class='jm'>{@link #add(Class,Object) add(Class,Object)}
055 *    <li class='jm'>{@link #add(Class,Object,String) add(Class,Object,String)}
056 *    <li class='jm'>{@link #addBean(Class,Object) addBean(Class,Object)}
057 *    <li class='jm'>{@link #addBean(Class,Object,String) addBean(Class,Object,String)}
058 *    <li class='jm'>{@link #addSupplier(Class,Supplier) addSupplier(Class,Supplier)}
059 *    <li class='jm'>{@link #addSupplier(Class,Supplier,String) addSupplier(Class,Supplier,String)}
060 * </ul>
061 *
062 * <p>
063 * Beans are retrieved through the following methods:
064 * <ul class='javatreec'>
065 *    <li class='jm'>{@link #getBean(Class) getBean(Class)}
066 *    <li class='jm'>{@link #getBean(Class,String) getBean(Class,String)}
067 *    <li class='jm'>{@link #stream(Class) stream(Class)}
068 * </ul>
069 *
070 * <p>
071 * Beans are created through the following methods:
072 * <ul class='javatreec'>
073 *    <li class='jm'>{@link #createBean(Class) createBean(Class)}
074 *    <li class='jm'>{@link #createMethodFinder(Class) createMethodFinder(Class)}
075 *    <li class='jm'>{@link #createMethodFinder(Class,Class) createMethodFinder(Class,Class)}
076 *    <li class='jm'>{@link #createMethodFinder(Class,Object) createMethodFinder(Class,Object)}
077 * </ul>
078 *
079 * <h5 class='section'>Notes:</h5><ul>
080 *    <li class='note'>Bean stores can be nested using {@link Builder#parent(BeanStore)}.
081 *    <li class='note'>Bean stores can be made read-only using {@link Builder#readOnly()}.
082 *    <li class='note'>Bean stores can be made thread-safe using {@link Builder#threadSafe()}.
083 * </ul>
084 *
085 * <h5 class='section'>See Also:</h5><ul>
086 * </ul>
087 */
088public class BeanStore {
089
090   //-----------------------------------------------------------------------------------------------------------------
091   // Static
092   //-----------------------------------------------------------------------------------------------------------------
093
094   /**
095    * Non-existent bean store.
096    */
097   public static final class Void extends BeanStore {}
098
099   /**
100    * Static read-only reusable instance.
101    */
102   public static final BeanStore INSTANCE = create().readOnly().build();
103
104   /**
105    * Static creator.
106    *
107    * @return A new {@link Builder} object.
108    */
109   public static Builder create() {
110      return new Builder();
111   }
112
113   /**
114    * Static creator.
115    *
116    * @param parent Parent bean store.  Can be <jk>null</jk> if this is the root resource.
117    * @return A new {@link BeanStore} object.
118    */
119   public static BeanStore of(BeanStore parent) {
120      return create().parent(parent).build();
121   }
122
123   /**
124    * Static creator.
125    *
126    * @param parent Parent bean store.  Can be <jk>null</jk> if this is the root resource.
127    * @param outer The outer bean used when instantiating inner classes.  Can be <jk>null</jk>.
128    * @return A new {@link BeanStore} object.
129    */
130   public static BeanStore of(BeanStore parent, Object outer) {
131      return create().parent(parent).outer(outer).build();
132   }
133
134   //-----------------------------------------------------------------------------------------------------------------
135   // Builder
136   //-----------------------------------------------------------------------------------------------------------------
137
138   /**
139    * Builder class.
140    */
141   public static class Builder {
142
143      BeanStore parent;
144      boolean readOnly, threadSafe;
145      Object outer;
146      Class<? extends BeanStore> type;
147      BeanStore impl;
148
149      /**
150       * Constructor.
151       */
152      protected Builder() {}
153
154      /**
155       * Instantiates this bean store.
156       *
157       * @return A new bean store.
158       */
159      public BeanStore build() {
160         if (impl != null)
161            return impl;
162         if (type == null || type == BeanStore.class)
163            return new BeanStore(this);
164
165         ClassInfo c = ClassInfo.of(type);
166
167         MethodInfo m = c.getDeclaredMethod(
168            x -> x.isPublic()
169            && x.hasNoParams()
170            && x.isStatic()
171            && x.hasName("getInstance")
172         );
173         if (m != null)
174            return m.invoke(null);
175
176         ConstructorInfo ci = c.getPublicConstructor(x -> x.canAccept(this));
177         if (ci != null)
178            return ci.invoke(this);
179
180         ci = c.getDeclaredConstructor(x -> x.isProtected() && x.canAccept(this));
181         if (ci != null)
182            return ci.accessible().invoke(this);
183
184         throw new BasicRuntimeException("Could not find a way to instantiate class {0}", type);
185      }
186
187      //-------------------------------------------------------------------------------------------------------------
188      // Properties
189      //-------------------------------------------------------------------------------------------------------------
190
191      /**
192       * Specifies the parent bean store.
193       *
194       * <p>
195       * Bean searches are performed recursively up this parent chain.
196       *
197       * @param value The setting value.
198       * @return  This object.
199       */
200      public Builder parent(BeanStore value) {
201         parent = value;
202         return this;
203      }
204
205      /**
206       * Specifies that the bean store is read-only.
207       *
208       * <p>
209       * This means methods such as {@link BeanStore#addBean(Class, Object)} cannot be used.
210       *
211       * @return  This object.
212       */
213      public Builder readOnly() {
214         readOnly = true;
215         return this;
216      }
217
218      /**
219       * Specifies that the bean store being created should be thread-safe.
220       *
221       * @return  This object.
222       */
223      public Builder threadSafe() {
224         threadSafe = true;
225         return this;
226      }
227
228      /**
229       * Specifies the outer bean context.
230       *
231       * <p>
232       * The outer context bean to use when calling constructors on inner classes.
233       *
234       * @param value The outer bean context.  Can be <jk>null</jk>.
235       * @return  This object.
236       */
237      public Builder outer(Object value) {
238         this.outer = value;
239         return this;
240      }
241
242      /**
243       * Overrides the bean to return from the {@link #build()} method.
244       *
245       * @param value The bean to return from the {@link #build()} method.
246       * @return This object.
247       */
248      public Builder impl(BeanStore value) {
249         this.impl = value;
250         return this;
251      }
252
253      /**
254       * Overrides the bean store type.
255       *
256       * <p>
257       * The specified type must have one of the following:
258       * <ul>
259       *    <li>A static <c>getInstance()</c> method.
260       *    <li>A public constructor that takes in this builder.
261       *    <li>A protected constructor that takes in this builder.
262       * </ul>
263       *
264       * @param value The bean store type.
265       * @return This object.
266       */
267      public Builder type(Class<? extends BeanStore> value) {
268         this.type = value;
269         return this;
270      }
271   }
272
273   //-----------------------------------------------------------------------------------------------------------------
274   // Instance
275   //-----------------------------------------------------------------------------------------------------------------
276
277   private final Deque<BeanStoreEntry<?>> entries;
278   private final Map<Class<?>,BeanStoreEntry<?>> unnamedEntries;
279
280   final Optional<BeanStore> parent;
281   final Optional<Object> outer;
282   final boolean readOnly, threadSafe;
283   final SimpleReadWriteLock lock;
284
285   BeanStore() {
286      this(create());
287   }
288
289   /**
290    * Constructor.
291    *
292    * @param builder The builder containing the settings for this bean.
293    */
294   protected BeanStore(Builder builder) {
295      parent = Utils.opt(builder.parent);
296      outer = Utils.opt(builder.outer);
297      readOnly = builder.readOnly;
298      threadSafe = builder.threadSafe;
299      lock = threadSafe ? new SimpleReadWriteLock() : SimpleReadWriteLock.NO_OP;
300      entries = threadSafe ? new ConcurrentLinkedDeque<>() : new LinkedList<>();
301      unnamedEntries = threadSafe ? new ConcurrentHashMap<>() : map();
302   }
303
304   /**
305    * Adds an unnamed bean of the specified type to this factory.
306    *
307    * @param <T> The class to associate this bean with.
308    * @param beanType The class to associate this bean with.
309    * @param bean The bean.  Can be <jk>null</jk>.
310    * @return This object.
311    */
312   public <T> BeanStore addBean(Class<T> beanType, T bean) {
313      return addBean(beanType, bean, null);
314   }
315
316   /**
317    * Adds a named bean of the specified type to this factory.
318    *
319    * @param <T> The class to associate this bean with.
320    * @param beanType The class to associate this bean with.
321    * @param bean The bean.  Can be <jk>null</jk>.
322    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
323    * @return This object.
324    */
325   public <T> BeanStore addBean(Class<T> beanType, T bean, String name) {
326      return addSupplier(beanType, ()->bean, name);
327   }
328
329   /**
330    * Adds a supplier for an unnamed bean of the specified type to this factory.
331    *
332    * @param <T> The class to associate this bean with.
333    * @param beanType The class to associate this bean with.
334    * @param bean The bean supplier.
335    * @return This object.
336    */
337   public <T> BeanStore addSupplier(Class<T> beanType, Supplier<T> bean) {
338      return addSupplier(beanType, bean, null);
339   }
340
341   /**
342    * Adds a supplier for a named bean of the specified type to this factory.
343    *
344    * @param <T> The class to associate this bean with.
345    * @param beanType The class to associate this bean with.
346    * @param bean The bean supplier.
347    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
348    * @return This object.
349    */
350   public <T> BeanStore addSupplier(Class<T> beanType, Supplier<T> bean, String name) {
351      assertCanWrite();
352      BeanStoreEntry<T> e = createEntry(beanType, bean, name);
353      try (SimpleLock x = lock.write()) {
354         entries.addFirst(e);
355         if (Utils.isEmpty(name))
356            unnamedEntries.put(beanType, e);
357      }
358      return this;
359   }
360
361   /**
362    * Same as {@link #addBean(Class,Object)} but returns the bean instead of this object for fluent calls.
363    *
364    * @param <T> The class to associate this bean with.
365    * @param beanType The class to associate this bean with.
366    * @param bean The bean.  Can be <jk>null</jk>.
367    * @return The bean.
368    */
369   public <T> T add(Class<T> beanType, T bean) {
370      add(beanType, bean, null);
371      return bean;
372   }
373
374   /**
375    * Same as {@link #addBean(Class,Object,String)} but returns the bean instead of this object for fluent calls.
376    *
377    * @param <T> The class to associate this bean with.
378    * @param beanType The class to associate this bean with.
379    * @param bean The bean.  Can be <jk>null</jk>.
380    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
381    * @return The bean.
382    */
383   public <T> T add(Class<T> beanType, T bean, String name) {
384      addBean(beanType, bean, name);
385      return bean;
386   }
387
388   /**
389    * Clears out all bean in this bean store.
390    *
391    * <p>
392    * Does not affect the parent bean store.
393    *
394    * @return This object.
395    */
396   public BeanStore clear() {
397      assertCanWrite();
398      try (SimpleLock x = lock.write()) {
399         unnamedEntries.clear();
400         entries.clear();
401      }
402      return this;
403   }
404
405   /**
406    * Returns the unnamed bean of the specified type.
407    *
408    * @param <T> The type of bean to return.
409    * @param beanType The type of bean to return.
410    * @return The bean.
411    */
412   @SuppressWarnings("unchecked")
413   public <T> Optional<T> getBean(Class<T> beanType) {
414      try (SimpleLock x = lock.read()) {
415         BeanStoreEntry<T> e = (BeanStoreEntry<T>) unnamedEntries.get(beanType);
416         if (e != null)
417            return Utils.opt(e.get());
418         if (parent.isPresent())
419            return parent.get().getBean(beanType);
420         return Utils.opte();
421      }
422   }
423
424   /**
425    * Returns the named bean of the specified type.
426    *
427    * @param <T> The type of bean to return.
428    * @param beanType The type of bean to return.
429    * @param name The bean name.  Can be <jk>null</jk>.
430    * @return The bean.
431    */
432   @SuppressWarnings("unchecked")
433   public <T> Optional<T> getBean(Class<T> beanType, String name)  {
434      try (SimpleLock x = lock.read()) {
435         BeanStoreEntry<T> e = (BeanStoreEntry<T>)entries.stream().filter(x2 -> x2.matches(beanType, name)).findFirst().orElse(null);
436         if (e != null)
437            return Utils.opt(e.get());
438         if (parent.isPresent())
439            return parent.get().getBean(beanType, name);
440         return Utils.opte();
441      }
442   }
443
444   /**
445    * Returns all the beans in this store of the specified type.
446    *
447    * <p>
448    * Returns both named and unnamed beans.
449    *
450    * <p>
451    * The results from the parent bean store are appended to the list of beans from this beans store.
452    *
453    * @param <T> The bean type to return.
454    * @param beanType The bean type to return.
455    * @return The bean entries.  Never <jk>null</jk>.
456    */
457   public <T> Stream<BeanStoreEntry<T>> stream(Class<T> beanType)  {
458      @SuppressWarnings("unchecked")
459      Stream<BeanStoreEntry<T>> s = entries.stream().filter(x -> x.matches(beanType)).map(x -> (BeanStoreEntry<T>)x);
460      if (parent.isPresent())
461         s = Stream.concat(s, parent.get().stream(beanType));
462      return s;
463   }
464
465   /**
466    * Removes an unnamed bean from this store.
467    *
468    * @param beanType The bean type being removed.
469    * @return This object.
470    */
471   public BeanStore removeBean(Class<?> beanType) {
472      return removeBean(beanType, null);
473   }
474
475   /**
476    * Removes a named bean from this store.
477    *
478    * @param beanType The bean type being removed.
479    * @param name The bean name to remove.
480    * @return This object.
481    */
482   public BeanStore removeBean(Class<?> beanType, String name) {
483      assertCanWrite();
484      try (SimpleLock x = lock.write()) {
485         if (name == null)
486            unnamedEntries.remove(beanType);
487         entries.removeIf(y -> y.matches(beanType, name));
488      }
489      return this;
490   }
491
492   /**
493    * Returns <jk>true</jk> if this store contains the specified unnamed bean type.
494    *
495    * @param beanType The bean type to check.
496    * @return <jk>true</jk> if this store contains the specified unnamed bean type.
497    */
498   public boolean hasBean(Class<?> beanType) {
499      return unnamedEntries.containsKey(beanType) || parent.map(x -> x.hasBean(beanType)).orElse(false);
500   }
501
502   /**
503    * Returns <jk>true</jk> if this store contains the specified named bean type.
504    *
505    * @param beanType The bean type to check.
506    * @param name The bean name.
507    * @return <jk>true</jk> if this store contains the specified named bean type.
508    */
509   public boolean hasBean(Class<?> beanType, String name) {
510      return entries.stream().anyMatch(x -> x.matches(beanType, name)) || parent.map(x -> x.hasBean(beanType, name)).orElse(false);
511   }
512
513   /**
514    * Instantiates a bean creator.
515    *
516    * <h5 class='section'>See Also:</h5><ul>
517    *    <li class='jc'>{@link BeanCreator} for usage.
518    * </ul>
519    *
520    * @param <T> The bean type to create.
521    * @param beanType The bean type to create.
522    * @return A new bean creator.
523    */
524   public <T> BeanCreator<T> createBean(Class<T> beanType) {
525      return new BeanCreator<>(beanType, this);
526   }
527
528   /**
529    * Create a method finder for finding bean creation methods.
530    *
531    * <h5 class='section'>See Also:</h5><ul>
532    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
533    * </ul>
534    *
535    * @param <T> The bean type to create.
536    * @param beanType The bean type to create.
537    * @param resource The class containing the bean creator method.
538    * @return The method finder.  Never <jk>null</jk>.
539    */
540   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType, Object resource) {
541      return new BeanCreateMethodFinder<>(beanType, resource, this);
542   }
543
544   /**
545    * Create a method finder for finding bean creation methods.
546    *
547    * <p>
548    * Same as {@link #createMethodFinder(Class,Class)} but looks for only static methods on the specified resource class
549    * and not also instance methods within the context of a bean.
550    *
551    * <h5 class='section'>See Also:</h5><ul>
552    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
553    * </ul>
554    *
555    * @param <T> The bean type to create.
556    * @param beanType The bean type to create.
557    * @param resourceClass The class containing the bean creator method.
558    * @return The method finder.  Never <jk>null</jk>.
559    */
560   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType, Class<?> resourceClass) {
561      return new BeanCreateMethodFinder<>(beanType, resourceClass , this);
562   }
563
564   /**
565    * Create a method finder for finding bean creation methods.
566    *
567    * <p>
568    * Same as {@link #createMethodFinder(Class,Object)} but uses {@link Builder#outer(Object)} as the resource bean.
569    *
570    * <h5 class='section'>See Also:</h5><ul>
571    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
572    * </ul>
573    *
574    * @param <T> The bean type to create.
575    * @param beanType The bean type to create.
576    * @return The method finder.  Never <jk>null</jk>.
577    */
578   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType) {
579      return new BeanCreateMethodFinder<>(beanType, outer.orElseThrow(() -> new IllegalArgumentException("Method cannot be used without outer bean definition.")), this);
580   }
581
582   /**
583    * Given an executable, returns a list of types that are missing from this factory.
584    *
585    * @param executable The constructor or method to get the params for.
586    * @return A comma-delimited list of types that are missing from this factory, or <jk>null</jk> if none are missing.
587    */
588   public String getMissingParams(ExecutableInfo executable) {
589      List<ParamInfo> params = executable.getParams();
590      List<String> l = list();
591      loop: for (int i = 0; i < params.size(); i++) {
592         ParamInfo pi = params.get(i);
593         ClassInfo pt = pi.getParameterType();
594         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get()))
595            continue loop;
596         if (pt.is(Optional.class) || pt.is(BeanStore.class))
597            continue loop;
598         String beanName = findBeanName(pi);
599         Class<?> ptc = pt.inner();
600         if (beanName == null && !hasBean(ptc))
601            l.add(pt.getSimpleName());
602         if (beanName != null && !hasBean(ptc, beanName))
603            l.add(pt.getSimpleName() + '@' + beanName);
604      }
605      return l.isEmpty() ? null : l.stream().sorted().collect(joining(","));
606   }
607
608   /**
609    * Given the list of param types, returns <jk>true</jk> if this factory has all the parameters for the specified executable.
610    *
611    * @param executable The constructor or method to get the params for.
612    * @return A comma-delimited list of types that are missing from this factory.
613    */
614   public boolean hasAllParams(ExecutableInfo executable) {
615      loop: for (int i = 0; i < executable.getParamCount(); i++) {
616         ParamInfo pi = executable.getParam(i);
617         ClassInfo pt = pi.getParameterType();
618         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get()))
619            continue loop;
620         if (pt.is(Optional.class) || pt.is(BeanStore.class))
621            continue loop;
622         String beanName = findBeanName(pi);
623         Class<?> ptc = pt.inner();
624         if ((beanName == null && !hasBean(ptc)) || (beanName != null && !hasBean(ptc, beanName)))
625            return false;
626      }
627      return true;
628   }
629
630
631   /**
632    * Returns the corresponding beans in this factory for the specified param types.
633    *
634    * @param executable The constructor or method to get the params for.
635    * @return The corresponding beans in this factory for the specified param types.
636    */
637   public Object[] getParams(ExecutableInfo executable) {
638      Object[] o = new Object[executable.getParamCount()];
639      for (int i = 0; i < executable.getParamCount(); i++) {
640         ParamInfo pi = executable.getParam(i);
641         ClassInfo pt = pi.getParameterType();
642         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get())) {
643            o[i] = outer.get();
644         } else if (pt.is(BeanStore.class)) {
645            o[i] = this;
646         } else {
647            String beanName = findBeanName(pi);
648            Class<?> ptc = pt.unwrap(Optional.class).inner();
649            Optional<?> o2 = beanName == null ? getBean(ptc) : getBean(ptc, beanName);
650            o[i] = pt.is(Optional.class) ? o2 : o2.orElse(null);
651         }
652      }
653      return o;
654   }
655
656   @Override /* Object */
657   public String toString() {
658      return Json5.of(properties());
659   }
660
661   //-----------------------------------------------------------------------------------------------------------------
662   // Extension methods
663   //-----------------------------------------------------------------------------------------------------------------
664
665   /**
666    * Creates an entry in this store for the specified bean.
667    *
668    * <p>
669    * Subclasses can override this method to create their own entry subtypes.
670    *
671    * @param <T> The class type to associate with the bean.
672    * @param type The class type to associate with the bean.
673    * @param bean The bean supplier.
674    * @param name Optional name to associate with the bean.  Can be <jk>null</jk>.
675    * @return A new bean store entry.
676    */
677   protected <T> BeanStoreEntry<T> createEntry(Class<T> type, Supplier<T> bean, String name) {
678      return BeanStoreEntry.create(type, bean, name);
679   }
680   //-----------------------------------------------------------------------------------------------------------------
681   // Helper methods
682   //-----------------------------------------------------------------------------------------------------------------
683   private String findBeanName(ParamInfo pi) {
684      Annotation n = pi.getAnnotation(Annotation.class, x -> x.annotationType().getSimpleName().equals("Named"));
685      if (n != null)
686         return AnnotationInfo.of((ClassInfo)null, n).getValue(String.class, "value", NOT_EMPTY).orElse(null);
687      return null;
688   }
689
690   private void assertCanWrite() {
691      if (readOnly)
692         throw new IllegalStateException("Method cannot be used because BeanStore is read-only.");
693   }
694
695   private JsonMap properties() {
696      Predicate<Boolean> nf = Utils::isTrue;
697      return filteredMap()
698         .append("identity", Utils2.identity(this))
699         .append("entries", entries.stream().map(BeanStoreEntry::properties).collect(toList()))
700         .append("outer", Utils2.identity(outer.orElse(null)))
701         .append("parent", parent.map(BeanStore::properties).orElse(null))
702         .appendIf(nf, "readOnly", readOnly)
703         .appendIf(nf, "threadSafe", threadSafe)
704      ;
705   }
706}