1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.localization.wiki.internal

File AbstractDocumentTranslationBundle.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
41% of files have more coverage

Code metrics

16
55
11
1
255
159
22
0.4
5
11
2

Classes

Class Line # Actions
AbstractDocumentTranslationBundle 66 55 0% 22 8
0.90243990.2%
 

Contributing tests

This file is covered by 11 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.localization.wiki.internal;
21   
22    import java.io.StringReader;
23    import java.util.Arrays;
24    import java.util.List;
25    import java.util.Locale;
26    import java.util.Map;
27    import java.util.Properties;
28   
29    import javax.inject.Provider;
30   
31    import org.slf4j.LoggerFactory;
32    import org.xwiki.bridge.event.DocumentCreatedEvent;
33    import org.xwiki.bridge.event.DocumentDeletedEvent;
34    import org.xwiki.bridge.event.DocumentUpdatedEvent;
35    import org.xwiki.bridge.event.WikiDeletedEvent;
36    import org.xwiki.cache.DisposableCacheValue;
37    import org.xwiki.component.manager.ComponentLookupException;
38    import org.xwiki.component.manager.ComponentManager;
39    import org.xwiki.component.phase.Disposable;
40    import org.xwiki.localization.Translation;
41    import org.xwiki.localization.TranslationBundle;
42    import org.xwiki.localization.TranslationBundleContext;
43    import org.xwiki.localization.internal.AbstractCachedTranslationBundle;
44    import org.xwiki.localization.internal.DefaultLocalizedTranslationBundle;
45    import org.xwiki.localization.internal.DefaultTranslation;
46    import org.xwiki.localization.internal.LocalizedTranslationBundle;
47    import org.xwiki.localization.message.TranslationMessage;
48    import org.xwiki.localization.message.TranslationMessageParser;
49    import org.xwiki.model.reference.DocumentReference;
50    import org.xwiki.model.reference.EntityReferenceSerializer;
51    import org.xwiki.observation.EventListener;
52    import org.xwiki.observation.ObservationManager;
53    import org.xwiki.observation.event.Event;
54   
55    import com.xpn.xwiki.XWiki;
56    import com.xpn.xwiki.XWikiContext;
57    import com.xpn.xwiki.doc.XWikiDocument;
58   
59    /**
60    * Base class for {@link TranslationBundle}s using wiki documents as resources. Provides methods for loading properties
61    * from documents, watching loaded documents and invalidating cached translations.
62    *
63    * @version $Id: 3db632c7a3df16e17f86e699e7a88a9e8e03e78c $
64    * @since 4.3M2
65    */
 
66    public abstract class AbstractDocumentTranslationBundle extends AbstractCachedTranslationBundle implements
67    DisposableCacheValue, Disposable, EventListener
68    {
69    /**
70    * Make default wiki document based translation priority a bit higher than the default one.
71    */
72    public static final int DEFAULTPRIORITY_WIKI = DEFAULTPRIORITY - 100;
73   
74    protected ComponentManager componentManager;
75   
76    protected TranslationBundleContext bundleContext;
77   
78    protected EntityReferenceSerializer<String> serializer;
79   
80    protected Provider<XWikiContext> contextProvider;
81   
82    protected ObservationManager observation;
83   
84    protected TranslationMessageParser translationMessageParser;
85   
86    protected List<Event> events;
87   
88    /**
89    * Indicate if it should try to access translations or not. A document bundle is usually in disposed state because
90    * the associated document does not exist anymore but something is still (wrongly) holding a reference to it.
91    */
92    protected boolean disposed;
93   
94    protected DocumentReference documentReference;
95   
96    /**
97    * The prefix to use when generating the bundle unique identifier.
98    */
99    protected String idPrefix;
100   
 
101  634 toggle public AbstractDocumentTranslationBundle(String idPrefix, DocumentReference reference,
102    ComponentManager componentManager, TranslationMessageParser translationMessageParser)
103    throws ComponentLookupException
104    {
105  634 this.idPrefix = idPrefix;
106   
107  634 this.componentManager = componentManager;
108  634 this.bundleContext = componentManager.getInstance(TranslationBundleContext.class);
109  634 this.serializer = componentManager.getInstance(EntityReferenceSerializer.TYPE_STRING);
110  634 this.contextProvider = componentManager.getInstance(XWikiContext.TYPE_PROVIDER);
111  634 this.observation = componentManager.getInstance(ObservationManager.class);
112   
113  634 this.translationMessageParser = translationMessageParser;
114   
115  634 this.logger = LoggerFactory.getLogger(getClass());
116   
117  634 setPriority(DEFAULTPRIORITY_WIKI);
118   
119  634 setReference(reference);
120   
121  634 initialize();
122    }
123   
 
124  634 toggle private void initialize()
125    {
126  634 this.events =
127    Arrays.<Event>asList(new DocumentUpdatedEvent(this.documentReference), new DocumentCreatedEvent(
128    this.documentReference), new DocumentDeletedEvent(this.documentReference), new WikiDeletedEvent(
129    this.documentReference.getWikiReference().getName()));
130   
131  634 this.observation.addListener(this);
132    }
133   
 
134  634 toggle protected void setReference(DocumentReference reference)
135    {
136  634 this.documentReference = reference;
137   
138  634 setId(this.idPrefix + this.serializer.serialize(reference));
139    }
140   
 
141  1409 toggle protected LocalizedTranslationBundle loadDocumentLocaleBundle(Locale locale) throws Exception
142    {
143  1409 XWikiContext context = this.contextProvider.get();
144   
145  1409 if (context == null) {
146    // No context for some reason, lets try later
147  0 return null;
148    }
149   
150  1409 XWiki xwiki = context.getWiki();
151   
152  1409 if (xwiki == null) {
153    // No XWiki instance ready, lets try later
154  0 return null;
155    }
156   
157  1409 XWikiDocument document = xwiki.getDocument(this.documentReference, context);
158   
159  1409 if (locale != null && !locale.equals(Locale.ROOT) && !locale.equals(document.getDefaultLocale())) {
160  93 document = xwiki.getDocument(new DocumentReference(document.getDocumentReference(), locale), context);
161   
162  93 if (document.isNew()) {
163    // No document found for this locale
164  60 return LocalizedTranslationBundle.EMPTY;
165    }
166    }
167   
168  1349 String content = document.getContent();
169   
170  1349 Properties properties = new Properties();
171  1349 properties.load(new StringReader(content));
172   
173    // Convert to LocalBundle
174  1349 DefaultLocalizedTranslationBundle localeBundle = new DefaultLocalizedTranslationBundle(this, locale);
175   
176  1349 TranslationMessageParser parser = getTranslationMessageParser();
177   
178  1349 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
179  42695 if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
180  42695 String key = (String) entry.getKey();
181  42695 String message = (String) entry.getValue();
182   
183  42695 TranslationMessage translationMessage = parser.parse(message);
184   
185  42695 localeBundle.addTranslation(new DefaultTranslation(this.bundleContext, localeBundle, key,
186    translationMessage));
187    }
188    }
189   
190  1349 return localeBundle;
191    }
192   
 
193  1349 toggle protected TranslationMessageParser getTranslationMessageParser()
194    {
195  1349 return this.translationMessageParser;
196    }
197   
 
198  1409 toggle @Override
199    protected LocalizedTranslationBundle createBundle(Locale locale) throws Exception
200    {
201  1409 return loadDocumentLocaleBundle(locale);
202    }
203   
 
204  129299664 toggle @Override
205    public Translation getTranslation(String key, Locale locale)
206    {
207  129299685 if (this.disposed) {
208  26996 return null;
209    }
210   
211  129272699 return super.getTranslation(key, locale);
212    }
213   
214    // DisposableCacheValue Disposable
215   
 
216  95 toggle @Override
217    public void dispose()
218    {
219  95 this.disposed = true;
220  95 this.bundleCache.clear();
221  95 this.observation.removeListener(getName());
222    }
223   
224    // EventListener
225   
 
226  580 toggle @Override
227    public void onEvent(Event event, Object source, Object data)
228    {
229  580 if (event instanceof WikiDeletedEvent) {
230  0 this.bundleCache.clear();
231   
232  0 this.disposed = true;
233    } else {
234  580 XWikiDocument document = (XWikiDocument) source;
235   
236  580 this.bundleCache.remove(document.getLocale());
237   
238  580 if (document.getLocale().equals(Locale.ROOT)) {
239  181 this.bundleCache.remove(document.getDefaultLocale());
240    }
241    }
242    }
243   
 
244  6435 toggle @Override
245    public String getName()
246    {
247  6435 return "localization.bundle." + getId();
248    }
249   
 
250  634 toggle @Override
251    public List<Event> getEvents()
252    {
253  634 return events;
254    }
255    }