1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.internal.objects.classes

File XClassMigratorListener.java

 

Coverage histogram

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

Code metrics

12
44
4
1
196
121
14
0.32
11
4
3.5

Classes

Class Line # Actions
XClassMigratorListener 59 44 0% 14 7
0.883333388.3%
 

Contributing tests

This file is covered by 59 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 com.xpn.xwiki.internal.objects.classes;
21   
22    import java.util.List;
23   
24    import javax.inject.Inject;
25    import javax.inject.Named;
26    import javax.inject.Provider;
27   
28    import org.slf4j.Logger;
29    import org.xwiki.model.EntityType;
30    import org.xwiki.model.reference.ClassPropertyReference;
31    import org.xwiki.model.reference.DocumentReferenceResolver;
32    import org.xwiki.model.reference.EntityReference;
33    import org.xwiki.model.reference.EntityReferenceSerializer;
34    import org.xwiki.observation.AbstractEventListener;
35    import org.xwiki.observation.event.Event;
36    import org.xwiki.query.Query;
37    import org.xwiki.query.QueryException;
38    import org.xwiki.query.QueryManager;
39   
40    import com.xpn.xwiki.XWikiContext;
41    import com.xpn.xwiki.XWikiException;
42    import com.xpn.xwiki.doc.XWikiDocument;
43    import com.xpn.xwiki.internal.event.XClassPropertyUpdatedEvent;
44    import com.xpn.xwiki.internal.store.PropertyConverter;
45    import com.xpn.xwiki.objects.BaseObject;
46    import com.xpn.xwiki.objects.BaseProperty;
47    import com.xpn.xwiki.objects.classes.PropertyClass;
48   
49    /**
50    * Listen to classes modifications and automatically update objects accordingly when needed.
51    * <p>
52    * The actual conversion is done in {@link PropertyConverter}.
53    *
54    * @version $Id: d5965a0910d3fe7bc2f8fab37bb9a920df6baa2a $
55    * @since 7.1RC1
56    */
57    // TODO: could probably be optimized a bit by listening to XClassUpdatedEvent and redoing the comparison between the two
58    // classes in case there is several changes to the class
 
59    public class XClassMigratorListener extends AbstractEventListener
60    {
61    @Inject
62    @Named("local")
63    private EntityReferenceSerializer<String> localSerializer;
64   
65    @Inject
66    private DocumentReferenceResolver<String> resolver;
67   
68    @Inject
69    private QueryManager queryManager;
70   
71    @Inject
72    private Provider<XWikiContext> xcontextProvider;
73   
74    /**
75    * Used for migrating the property values after a class is modified.
76    */
77    @Inject
78    private PropertyConverter propertyConverter;
79   
80    @Inject
81    private Logger logger;
82   
83    /**
84    * Setup the listener.
85    */
 
86  186 toggle public XClassMigratorListener()
87    {
88  186 super(XClassMigratorListener.class.getName(), new XClassPropertyUpdatedEvent());
89    }
90   
 
91  6 toggle @Override
92    public void onEvent(Event event, Object source, Object data)
93    {
94  6 XClassPropertyUpdatedEvent propertyEvent = (XClassPropertyUpdatedEvent) event;
95  6 XWikiDocument newDocument = (XWikiDocument) source;
96  6 XWikiDocument previousDocument = newDocument.getOriginalDocument();
97   
98  6 PropertyClass newPropertyClass =
99    (PropertyClass) newDocument.getXClass().getField(propertyEvent.getReference().getName());
100  6 PropertyClass previousPropertyClass =
101    (PropertyClass) previousDocument.getXClass().getField(propertyEvent.getReference().getName());
102   
103  6 if (newPropertyClass != null && previousPropertyClass != null) {
104  6 BaseProperty newProperty = newPropertyClass.newProperty();
105  6 BaseProperty previousProperty = previousPropertyClass.newProperty();
106   
107    // New and previous class property generate different kind of properties
108  6 if (newProperty.getClass() != previousProperty.getClass()) {
109  1 try {
110  1 migrate(newPropertyClass);
111    } catch (QueryException e) {
112  0 this.logger.error("Failed to migrate XClass property [{}]", newPropertyClass.getReference(), e);
113    }
114    }
115    }
116    }
117   
 
118  1 toggle private void migrate(PropertyClass newPropertyClass) throws QueryException
119    {
120  1 ClassPropertyReference propertyReference = newPropertyClass.getReference();
121  1 EntityReference classReference = propertyReference.extractReference(EntityType.DOCUMENT);
122  1 EntityReference wikiReference = propertyReference.extractReference(EntityType.WIKI);
123   
124    // Get all document containing object of modified class
125  1 Query query =
126    this.queryManager.createQuery("from doc.object(" + this.localSerializer.serialize(classReference)
127    + ") as obj", Query.XWQL);
128  1 query.setWiki(wikiReference.getName());
129   
130  1 List<String> documents = query.execute();
131   
132  1 if (!documents.isEmpty()) {
133  1 XWikiContext xcontext = this.xcontextProvider.get();
134   
135  1 String currentWikiId = xcontext.getWikiId();
136  1 try {
137    // Switch to class wiki to be safer
138  1 xcontext.setWikiId(wikiReference.getName());
139   
140  1 for (String documentName : documents) {
141  1 try {
142  1 migrate(newPropertyClass, documentName, xcontext);
143    } catch (XWikiException e) {
144  0 this.logger.error("Failed to migrate property [{}] in document [{}]", propertyReference,
145    documentName, xcontext);
146    }
147    }
148    } finally {
149    // Restore context wiki
150  1 xcontext.setWikiId(currentWikiId);
151    }
152    }
153    }
154   
 
155  1 toggle private void migrate(PropertyClass newPropertyClass, String documentName, XWikiContext xcontext)
156    throws XWikiException
157    {
158  1 BaseProperty newProperty = newPropertyClass.newProperty();
159   
160  1 ClassPropertyReference propertyReference = newPropertyClass.getReference();
161  1 EntityReference classReference = propertyReference.extractReference(EntityType.DOCUMENT);
162   
163  1 XWikiDocument document =
164    xcontext.getWiki().getDocument(this.resolver.resolve(documentName, classReference), xcontext);
165   
166  1 boolean modified = false;
167   
168  1 for (BaseObject xobject : document.getXObjects(classReference)) {
169  1 BaseProperty property = (BaseProperty) xobject.getField(propertyReference.getName());
170   
171    // If the existing field is of different kind than what is produced by the new class property
172  1 if (property != null && property.getClass() != newProperty.getClass()) {
173  1 BaseProperty<?> convertedProperty = this.propertyConverter.convertProperty(property, newPropertyClass);
174   
175    // Set new field
176  1 if (convertedProperty != null) {
177    // Mark old field for removal, only if the conversion was successful, to avoid losing data.
178  1 xobject.removeField(propertyReference.getName());
179   
180    // Don't set the new property if it's null (it means the property is not set).
181  1 xobject.safeput(propertyReference.getName(), convertedProperty);
182   
183  1 modified = true;
184    }
185    }
186    }
187   
188    // If anything changed save the document
189  1 if (modified) {
190  1 xcontext.getWiki().saveDocument(
191    document,
192    "Migrated property [" + propertyReference.getName() + "] from class ["
193    + this.localSerializer.serialize(classReference) + "]", xcontext);
194    }
195    }
196    }