1   /*
2    * Copyright 2003 - 2014 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.admin.common;
22  
23  import java.io.Serializable;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.TreeMap;
30  import java.util.UUID;
31  
32  import org.apache.commons.lang.builder.ToStringBuilder;
33  import org.apache.commons.lang3.text.ExtendedMessageFormat;
34  import org.efaps.admin.user.Company;
35  import org.efaps.ci.CIAdmin;
36  import org.efaps.ci.CIAdminCommon;
37  import org.efaps.db.Context;
38  import org.efaps.db.InstanceQuery;
39  import org.efaps.db.MultiPrintQuery;
40  import org.efaps.db.QueryBuilder;
41  import org.efaps.util.EFapsException;
42  import org.efaps.util.MsgFormat;
43  import org.efaps.util.cache.CacheLogListener;
44  import org.efaps.util.cache.CacheObjectInterface;
45  import org.efaps.util.cache.InfinispanCache;
46  import org.infinispan.Cache;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * TODO comment!
52   *
53   * @author The eFaps Team
54   * @version $Id$
55   */
56  public final class MsgPhrase
57      implements CacheObjectInterface, Serializable
58  {
59  
60      /**
61       * Used for serialization.
62       */
63      private static final long serialVersionUID = 1L;
64  
65      /**
66       * Logging instance used in this class.
67       */
68      private static final Logger LOG = LoggerFactory.getLogger(MsgPhrase.class);
69  
70      /**
71       * Name of the Cache by UUID.
72       */
73      private static final String UUIDCACHE = MsgPhrase.class.getName() + ".UUID";
74  
75      /**
76       * Name of the Cache by ID.
77       */
78      private static final String IDCACHE = MsgPhrase.class.getName() + ".ID";
79  
80      /**
81       * Name of the Cache by Name.
82       */
83      private static final String NAMECACHE = MsgPhrase.class.getName() + ".Name";
84  
85      /**
86       * Name of the Cache by Name.
87       */
88      private static final String ARGUMENTCACHE = MsgPhrase.class.getName() + ".Argument";
89  
90      /**
91       * Name of the Cache by Name.
92       */
93      private static final String LABELCACHE = MsgPhrase.class.getName() + ".Label";
94  
95      /**
96       * The instance variable stores the id of this SystemAttribute.
97       *
98       * @see #getId()
99       */
100     private final long id;
101 
102     /**
103      * The instance variable stores the UUID of this SystemAttribute.
104      *
105      * @see #getUUID()
106      */
107     private final UUID uuid;
108 
109     /**
110      * The instance variable stores the Name of this SystemAttribute.
111      *
112      * @see #getName()
113      */
114     private final String name;
115 
116     /**
117      * Labels.
118      */
119     private final Set<Label> labels = new HashSet<>();
120 
121     /**
122      * Arguments.
123      */
124     private final Set<Argument> arguments = new HashSet<>();
125 
126     /**
127      * Id fo the parent.
128      */
129     private final Long parentId;
130 
131     /**
132      * Constructor setting instance variables.
133      *
134      * @param _id id of the MsgPhrase
135      * @param _uuid uuid of the MsgPhrase
136      * @param _name name of the MsgPhrase
137      * @param _parentId if ot the parent
138      */
139     private MsgPhrase(final long _id,
140                       final String _name,
141                       final String _uuid,
142                       final Long _parentId)
143     {
144         this.id = _id;
145         this.uuid = UUID.fromString(_uuid);
146         this.name = _name;
147         this.parentId = _parentId;
148     }
149 
150     /**
151      * {@inheritDoc}
152      */
153     @Override
154     public String getName()
155     {
156         return this.name;
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     @Override
163     public UUID getUUID()
164     {
165         return this.uuid;
166     }
167 
168     /**
169      * {@inheritDoc}
170      */
171     @Override
172     public long getId()
173     {
174         return this.id;
175     }
176 
177     /**
178      * load the phrase.
179      * @throws EFapsException on error
180      */
181     private void load()
182         throws EFapsException
183     {
184         final QueryBuilder queryBldr = new QueryBuilder(CIAdminCommon.MsgPhraseConfigAbstract);
185         queryBldr.addWhereAttrEqValue(CIAdminCommon.MsgPhraseConfigAbstract.AbstractLink, getId());
186         final MultiPrintQuery multi = queryBldr.getPrint();
187         multi.addAttribute(CIAdminCommon.MsgPhraseConfigAbstract.CompanyLink,
188                         CIAdminCommon.MsgPhraseConfigAbstract.LanguageLink,
189                         CIAdminCommon.MsgPhraseConfigAbstract.Value,
190                         CIAdminCommon.MsgPhraseConfigAbstract.Int1);
191         multi.executeWithoutAccessCheck();
192         while (multi.next()) {
193             AbstractConfig conf = null;
194             if (multi.getCurrentInstance().getType().isCIType(CIAdminCommon.MsgPhraseArgument)) {
195                 conf = new Argument();
196                 ((Argument) conf).setIndex(multi.<Integer>getAttribute(CIAdminCommon.MsgPhraseConfigAbstract.Int1));
197                 this.arguments.add((Argument) conf);
198             } else if (multi.getCurrentInstance().getType().isCIType(CIAdminCommon.MsgPhraseLabel)) {
199                 conf = new Label();
200                 this.labels.add((Label) conf);
201             }
202             if (conf == null) {
203                 LOG.error("Wrong type: ", this);
204             } else {
205                 conf.setCompanyId(multi.<Long>getAttribute(CIAdminCommon.MsgPhraseConfigAbstract.CompanyLink));
206                 conf.setLanguageId(multi.<Long>getAttribute(CIAdminCommon.MsgPhraseConfigAbstract.LanguageLink));
207                 conf.setValue(multi.<String>getAttribute(CIAdminCommon.MsgPhraseConfigAbstract.Value));
208             }
209         }
210     }
211 
212     /**
213      * @return the label
214      * @throws EFapsException on error
215      */
216     public String getLabel()
217         throws EFapsException
218     {
219         return getLabel(Context.getThreadContext().getLanguage(), Context.getThreadContext().getCompany());
220     }
221 
222     /**
223      * @param _language language
224      * @param _company  Comany
225      * @return the label
226      * @throws EFapsException on error
227      */
228     public String getLabel(final String _language,
229                            final Company _company)
230         throws EFapsException
231     {
232         String ret = "empty";
233         final Cache<String, String> cache = InfinispanCache.get().<String, String>getCache(
234                         MsgPhrase.LABELCACHE);
235         final String key = _language + "_" + _company.getId() + "_" + getUUID().toString();
236         if (cache.containsKey(key)) {
237             ret = cache.get(key);
238         } else {
239             long languageid = 0;
240             final QueryBuilder queryBldr = new QueryBuilder(CIAdmin.Language);
241             queryBldr.addWhereAttrEqValue(CIAdmin.Language.Language, _language);
242             final InstanceQuery query = queryBldr.getQuery();
243             query.executeWithoutAccessCheck();
244             if (query.next()) {
245                 languageid = query.getCurrentValue().getId();
246             }
247             Label current = null;
248             MsgPhrase phrase = this;
249             while (phrase != null) {
250                 for (final Label label : phrase.labels) {
251                     if (current == null) {
252                         current = label;
253                     } else {
254                         if (label.getPriority(languageid, _company.getId()) > current.getPriority(languageid,
255                                         _company.getId())) {
256                             current = label;
257                         }
258                     }
259                 }
260                 phrase = phrase.getParent();
261             }
262             if (current != null) {
263                 ret = current.getValue();
264                 cache.put(key, ret);
265             }
266         }
267         return ret;
268     }
269 
270     /**
271      * @return list of arguments
272      * @throws EFapsException on error
273      */
274     public List<String> getArguments()
275         throws EFapsException
276     {
277         return getArguments(Context.getThreadContext().getLanguage(), Context.getThreadContext().getCompany());
278     }
279 
280      /**
281      * @param _language Language
282      * @param _company  Company
283      * @return list of arguments
284      * @throws EFapsException on error
285      */
286     public List<String> getArguments(final String _language,
287                                      final Company _company)
288         throws EFapsException
289     {
290         final List<String> ret = new ArrayList<>();
291         final Cache<String, List<String>> cache = InfinispanCache.get().<String, List<String>>getCache(
292                         MsgPhrase.ARGUMENTCACHE);
293         final String key = _language + "_" + _company.getId() + "_" + getUUID().toString();
294         if (cache.containsKey(key)) {
295             ret.addAll(cache.get(key));
296         } else {
297             long languageid = 0;
298             final QueryBuilder queryBldr = new QueryBuilder(CIAdmin.Language);
299             queryBldr.addWhereAttrEqValue(CIAdmin.Language.Language, _language);
300             final InstanceQuery query = queryBldr.getQuery();
301             query.executeWithoutAccessCheck();
302             if (query.next()) {
303                 languageid = query.getCurrentValue().getId();
304             }
305             final Map<Integer, Argument> orderMap = new TreeMap<>();
306 
307             MsgPhrase phrase = this;
308             while (phrase != null) {
309                 for (final Argument argument : phrase.arguments) {
310                     if (orderMap.containsKey(argument.getIndex())) {
311                         final Argument current = orderMap.get(argument.getIndex());
312                         if (argument.getPriority(languageid, _company.getId()) > current.getPriority(languageid,
313                                         _company.getId())) {
314                             orderMap.put(argument.getIndex(), argument);
315                         }
316                     } else {
317                         orderMap.put(argument.getIndex(), argument);
318                     }
319                 }
320                 phrase = phrase.getParent();
321             }
322             for (final Argument argument : orderMap.values()) {
323                 ret.add(argument.getValue());
324             }
325             cache.put(key, ret);
326         }
327         return ret;
328     }
329 
330     /**
331      * Getter method for the instance variable {@link #parentId}.
332      *
333      * @return value of instance variable {@link #parentId}
334      */
335     public Long getParentId()
336     {
337         return this.parentId;
338     }
339 
340     /**
341      * Getter method for the instance variable {@link #parentId}.
342      *
343      * @return value of instance variable {@link #parentId}
344      */
345     public boolean hasParent()
346     {
347         return getParentId() != null;
348     }
349 
350     /**
351      * Getter method for the instance variable {@link #parentId}.
352      *
353      * @return value of instance variable {@link #parentId}
354      * @throws EFapsException on error
355      */
356     public MsgPhrase getParent()
357         throws EFapsException
358     {
359         MsgPhrase ret;
360         if (hasParent()) {
361             ret = MsgPhrase.get(getParentId());
362         } else {
363             ret = null;
364         }
365         return ret;
366     }
367 
368     /**
369      * @param _object objects to be passed on to the Messageformatter
370      * @return formatted String
371      * @throws EFapsException on error
372      */
373     public String format(final Object... _object)
374         throws EFapsException
375     {
376         return format(Context.getThreadContext().getLanguage(), Context.getThreadContext().getCompany(), _object);
377     }
378 
379     /**
380      * @param _language Language
381      * @param _company  Company
382      * @param _object objects to be passed on to the Messageformatter
383      * @return formatted String
384      * @throws EFapsException on error
385      */
386     public String format(final String _language,
387                          final Company _company,
388                          final Object... _object)
389         throws EFapsException
390     {
391         final ExtendedMessageFormat frmt = MsgFormat.getFormat(getLabel(_language, _company));
392         return frmt.format(_object);
393     }
394 
395     @Override
396     public boolean equals(final Object _obj)
397     {
398         boolean ret;
399         if (_obj instanceof MsgPhrase) {
400             ret = ((MsgPhrase) _obj).getId() == getId();
401         } else {
402             ret = super.equals(_obj);
403         }
404         return ret;
405     }
406 
407     @Override
408     public int hashCode()
409     {
410         return Long.valueOf(getId()).intValue();
411     }
412 
413     @Override
414     public String toString()
415     {
416         return ToStringBuilder.reflectionToString(this);
417     }
418 
419     /**
420      * Returns for given parameter <i>_id</i> the instance of class
421      * {@link MsgPhrase}.
422      *
423      * @param _id id of the system configuration
424      * @return instance of class {@link MsgPhrase}
425      * @throws EFapsException on error
426      */
427     public static MsgPhrase get(final long _id)
428         throws EFapsException
429     {
430         final Cache<Long, MsgPhrase> cache = InfinispanCache.get().<Long, MsgPhrase>getCache(MsgPhrase.IDCACHE);
431         if (!cache.containsKey(_id)) {
432             MsgPhrase.loadMsgPhrase(_id);
433         }
434         return cache.get(_id);
435     }
436 
437     /**
438      * Returns for given parameter <i>_name</i> the instance of class
439      * {@link MsgPhrase}.
440      *
441      * @param _name name of the system configuration
442      * @return instance of class {@link MsgPhrase}
443      * @throws EFapsException on error
444      */
445     public static MsgPhrase get(final String _name)
446         throws EFapsException
447     {
448         final Cache<String, MsgPhrase> cache = InfinispanCache.get().<String, MsgPhrase>getCache(MsgPhrase.NAMECACHE);
449         if (!cache.containsKey(_name)) {
450             MsgPhrase.loadMsgPhrase(_name);
451         }
452         return cache.get(_name);
453     }
454 
455     /**
456      * Returns for given parameter <i>_uuid</i> the instance of class
457      * {@link MsgPhrase}.
458      *
459      * @param _uuid uuid of the system configuration
460      * @return instance of class {@link MsgPhrase}
461      * @throws EFapsException on error
462      */
463     public static MsgPhrase get(final UUID _uuid)
464         throws EFapsException
465     {
466         final Cache<UUID, MsgPhrase> cache = InfinispanCache.get().<UUID, MsgPhrase>getCache(MsgPhrase.UUIDCACHE);
467         if (!cache.containsKey(_uuid)) {
468             MsgPhrase.loadMsgPhrase(_uuid);
469         }
470         return cache.get(_uuid);
471     }
472 
473     /**
474      * Method to initialize the {@link #CACHE cache} for the system
475      * configurations.
476      */
477     public static void initialize()
478     {
479         if (InfinispanCache.get().exists(MsgPhrase.UUIDCACHE)) {
480             InfinispanCache.get().<UUID, MsgPhrase>getCache(MsgPhrase.UUIDCACHE).clear();
481         } else {
482             InfinispanCache.get().<UUID, MsgPhrase>getCache(MsgPhrase.UUIDCACHE)
483                             .addListener(new CacheLogListener(MsgPhrase.LOG));
484         }
485         if (InfinispanCache.get().exists(MsgPhrase.IDCACHE)) {
486             InfinispanCache.get().<Long, MsgPhrase>getCache(MsgPhrase.IDCACHE).clear();
487         } else {
488             InfinispanCache.get().<Long, MsgPhrase>getCache(MsgPhrase.IDCACHE)
489                             .addListener(new CacheLogListener(MsgPhrase.LOG));
490         }
491         if (InfinispanCache.get().exists(MsgPhrase.NAMECACHE)) {
492             InfinispanCache.get().<String, MsgPhrase>getCache(MsgPhrase.NAMECACHE).clear();
493         } else {
494             InfinispanCache.get().<String, MsgPhrase>getCache(MsgPhrase.NAMECACHE)
495                             .addListener(new CacheLogListener(MsgPhrase.LOG));
496         }
497         if (InfinispanCache.get().exists(MsgPhrase.LABELCACHE)) {
498             InfinispanCache.get().getCache(MsgPhrase.LABELCACHE).clear();
499         } else {
500             InfinispanCache.get().<String, String>getCache(MsgPhrase.LABELCACHE)
501                             .addListener(new CacheLogListener(MsgPhrase.LOG));
502         }
503         if (InfinispanCache.get().exists(MsgPhrase.ARGUMENTCACHE)) {
504             InfinispanCache.get().getCache(MsgPhrase.ARGUMENTCACHE).clear();
505         } else {
506             InfinispanCache.get().<String, List<String>>getCache(MsgPhrase.ARGUMENTCACHE)
507                             .addListener(new CacheLogListener(MsgPhrase.LOG));
508         }
509     }
510 
511     /**
512      * @param _phrase MsgPhrase to be cached
513      */
514     private static void cacheMsgPhrase(final MsgPhrase _phrase)
515     {
516         final Cache<UUID, MsgPhrase> cache4UUID = InfinispanCache.get()
517                         .<UUID, MsgPhrase>getIgnReCache(MsgPhrase.UUIDCACHE);
518         cache4UUID.put(_phrase.getUUID(), _phrase);
519 
520         final Cache<String, MsgPhrase> nameCache = InfinispanCache.get()
521                         .<String, MsgPhrase>getIgnReCache(MsgPhrase.NAMECACHE);
522         nameCache.put(_phrase.getName(), _phrase);
523 
524         final Cache<Long, MsgPhrase> idCache = InfinispanCache.get()
525                         .<Long, MsgPhrase>getIgnReCache(MsgPhrase.IDCACHE);
526         idCache.putIfAbsent(_phrase.getId(), _phrase);
527     }
528 
529 
530     /**
531      * @param _criteria criteria to use
532      * @return true
533      * @throws EFapsException on error
534      */
535     private static boolean loadMsgPhrase(final Object _criteria)
536         throws EFapsException
537     {
538         final boolean ret = false;
539         final QueryBuilder queryBldr = new QueryBuilder(CIAdminCommon.MsgPhrase);
540         if (_criteria instanceof Long) {
541             queryBldr.addWhereAttrEqValue(CIAdminCommon.MsgPhrase.ID, _criteria);
542         } else if (_criteria instanceof UUID) {
543             queryBldr.addWhereAttrEqValue(CIAdminCommon.MsgPhrase.UUID, _criteria.toString());
544         } else {
545             queryBldr.addWhereAttrEqValue(CIAdminCommon.MsgPhrase.Name, _criteria);
546         }
547         final MultiPrintQuery multi = queryBldr.getPrint();
548         multi.addAttribute(CIAdminCommon.MsgPhrase.ID, CIAdminCommon.MsgPhrase.UUID, CIAdminCommon.MsgPhrase.Name,
549                         CIAdminCommon.MsgPhrase.ParentLink);
550         multi.executeWithoutAccessCheck();
551         if (multi.next()) {
552             final MsgPhrase phrase = new MsgPhrase(multi.<Long>getAttribute(CIAdminCommon.MsgPhrase.ID),
553                             multi.<String>getAttribute(CIAdminCommon.MsgPhrase.Name),
554                             multi.<String>getAttribute(CIAdminCommon.MsgPhrase.UUID),
555                             multi.<Long>getAttribute(CIAdminCommon.MsgPhrase.ParentLink));
556             phrase.load();
557             cacheMsgPhrase(phrase);
558         }
559         if (multi.next()) {
560             LOG.error("MsgPhrase is not unique for criteria: {}", _criteria);
561         }
562         return ret;
563     }
564 
565     /**
566      * Object class.
567      */
568     public abstract static class AbstractConfig
569     {
570 
571         /**
572          * Id of the language.
573          */
574         private long languageId = 0;
575 
576         /**
577          * Id of the company.
578          */
579         private long companyId = 0;
580 
581         /**
582          * value.
583          */
584         private String value;
585 
586         /**
587          * Setter method for instance variable {@link #companyId}.
588          *
589          * @param _companyId value for instance variable {@link #companyId}
590          */
591         public void setCompanyId(final Long _companyId)
592         {
593             if (_companyId != null) {
594                 this.companyId = _companyId;
595             }
596         }
597 
598         /**
599          * Getter method for the instance variable {@link #languageId}.
600          *
601          * @return value of instance variable {@link #languageId}
602          */
603         public long getLanguageId()
604         {
605             return this.languageId;
606         }
607 
608         /**
609          * Setter method for instance variable {@link #languageId}.
610          *
611          * @param _languageId value for instance variable {@link #languageId}
612          */
613         public void setLanguageId(final Long _languageId)
614         {
615             if (_languageId != null) {
616                 this.languageId = _languageId;
617             }
618         }
619 
620         /**
621          * Getter method for the instance variable {@link #companyId}.
622          *
623          * @return value of instance variable {@link #companyId}
624          */
625         public long getCompanyId()
626         {
627             return this.companyId;
628         }
629 
630         /**
631          * Getter method for the instance variable {@link #value}.
632          *
633          * @return value of instance variable {@link #value}
634          */
635         public String getValue()
636         {
637             return this.value;
638         }
639 
640         /**
641          * Setter method for instance variable {@link #value}.
642          *
643          * @param _value value for instance variable {@link #value}
644          */
645         public void setValue(final String _value)
646         {
647             this.value = _value;
648         }
649 
650         /**
651          * @param _languageId id of the language
652          * @param _companyId id for the company
653          * @return priority
654          */
655         public int getPriority(final Long _languageId,
656                                final Long _companyId)
657         {
658             int ret = 0;
659             if (_languageId == getLanguageId()) {
660                 ret = ret + 60;
661             }
662             if (_companyId == getCompanyId()) {
663                 ret = ret + 40;
664             }
665             return ret;
666         }
667     }
668 
669     /**
670      * Label object.
671      */
672     public static class Label
673         extends AbstractConfig
674     {
675 
676     }
677 
678     /**
679      * Argument Object.
680      */
681     public static class Argument
682         extends AbstractConfig
683     {
684 
685         /**
686          * Index.
687          */
688         private int index = 0;
689 
690         /**
691          * Getter method for the instance variable {@link #index}.
692          *
693          * @return value of instance variable {@link #index}
694          */
695         public int getIndex()
696         {
697             return this.index;
698         }
699 
700         /**
701          * Setter method for instance variable {@link #index}.
702          *
703          * @param _index value for instance variable {@link #index}
704          */
705         public void setIndex(final int _index)
706         {
707             this.index = _index;
708         }
709 
710     }
711 }