1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.importexport.postprocessors;
35
36 import info.magnolia.cms.core.MetaData;
37 import info.magnolia.jcr.util.NodeTypes;
38 import info.magnolia.jcr.util.NodeUtil;
39
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Map;
44 import java.util.Set;
45
46 import javax.jcr.Node;
47 import javax.jcr.NodeIterator;
48 import javax.jcr.Property;
49 import javax.jcr.PropertyIterator;
50 import javax.jcr.RepositoryException;
51
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60
61
62 public class MetaDataAsMixinConversionHelper {
63
64 private static final int PERIODIC_SAVE_FREQUENCY = 20;
65 private static final String DEPRECATED_DELETION_DATE_PROPERTY_NAME = "mgnl:deletedOn";
66
67 private final Logger logger = LoggerFactory.getLogger(MetaDataAsMixinConversionHelper.class);
68
69 private final HashMap<String, String> propertyNameMapping = new HashMap<String, String>();
70
71
72
73
74 private final Set<String> propertyNamesExpectedInMetaDataAfterConversion = new HashSet<String>();
75
76 private boolean deleteMetaDataIfEmptied = false;
77 private boolean periodicSaves = false;
78
79 public MetaDataAsMixinConversionHelper() {
80 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.CREATION_DATE, NodeTypes.Created.CREATED);
81 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.LAST_ACTION, NodeTypes.Activatable.LAST_ACTIVATED);
82 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.ACTIVATOR_ID, NodeTypes.Activatable.LAST_ACTIVATED_BY);
83 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.ACTIVATED, NodeTypes.Activatable.ACTIVATION_STATUS);
84 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.TEMPLATE, NodeTypes.Renderable.TEMPLATE);
85 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.LAST_MODIFIED, NodeTypes.LastModified.LAST_MODIFIED);
86 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.AUTHOR_ID, NodeTypes.LastModified.LAST_MODIFIED_BY);
87 propertyNameMapping.put("mgnl:comment", NodeTypes.Versionable.COMMENT);
88
89
90 propertyNamesExpectedInMetaDataAfterConversion.add(NodeTypes.MGNL_PREFIX + MetaData.TITLE);
91 propertyNamesExpectedInMetaDataAfterConversion.add(NodeTypes.MGNL_PREFIX + MetaData.TEMPLATE_TYPE);
92
93
94
95 propertyNamesExpectedInMetaDataAfterConversion.addAll(propertyNameMapping.keySet());
96 }
97
98 public boolean isDeleteMetaDataIfEmptied() {
99 return deleteMetaDataIfEmptied;
100 }
101
102 public void setDeleteMetaDataIfEmptied(boolean deleteMetaDataIfEmptied) {
103 this.deleteMetaDataIfEmptied = deleteMetaDataIfEmptied;
104 }
105
106 public boolean isPeriodicSaves() {
107 return periodicSaves;
108 }
109
110
111
112
113 public void setPeriodicSaves(boolean periodicSaves) {
114 this.periodicSaves = periodicSaves;
115 }
116
117 public void convertNodeAndChildren(Node startNode) throws RepositoryException {
118
119 int nodesProcessed = 0;
120 ArrayList<Node> nodes = new ArrayList<Node>();
121 nodes.add(startNode);
122
123 while (!nodes.isEmpty()) {
124
125
126 Node node = nodes.remove(nodes.size() - 1);
127
128 processNode(node);
129
130
131 nodesProcessed++;
132 if (periodicSaves && nodesProcessed % PERIODIC_SAVE_FREQUENCY == 0) {
133 node.getSession().save();
134 }
135
136
137 NodeIterator children = node.getNodes();
138 while (children.hasNext()) {
139 Node child = children.nextNode();
140 if (!(child.getName().equals(MetaData.DEFAULT_META_NODE) && NodeUtil.isNodeType(child, NodeTypes.MetaData.NAME))) {
141 nodes.add(child);
142 }
143 }
144 }
145 }
146
147 private void processNode(Node node) throws RepositoryException {
148
149
150 if (node.hasProperty(DEPRECATED_DELETION_DATE_PROPERTY_NAME)) {
151 moveProperty(node, DEPRECATED_DELETION_DATE_PROPERTY_NAME, node, NodeTypes.Deleted.DELETED);
152 }
153
154
155 if (node.hasNode(MetaData.DEFAULT_META_NODE)) {
156 Node metaDataNode = node.getNode(MetaData.DEFAULT_META_NODE);
157 if (NodeUtil.isNodeType(metaDataNode, NodeTypes.MetaData.NAME)) {
158 moveProperties(node, metaDataNode, propertyNameMapping);
159
160 if (deleteMetaDataIfEmptied && isEmptyMetaDataNode(metaDataNode)) {
161 metaDataNode.remove();
162 }
163 }
164 }
165 }
166
167
168
169
170 private boolean isEmptyMetaDataNode(Node node) throws RepositoryException {
171 if (node.getNodes().getSize() != 0) {
172 logger.warn("MetaData node not removed because it has sub nodes {}", node.getPath());
173 return false;
174 }
175 PropertyIterator iterator = node.getProperties();
176 while (iterator.hasNext()) {
177 Property property = iterator.nextProperty();
178 if (!isExpectedMetaDataProperty(property)) {
179 logger.warn("MetaData node not removed because of unrecognized property: {}", property.getPath());
180 return false;
181 }
182 }
183 return true;
184 }
185
186
187
188
189 private boolean isExpectedMetaDataProperty(Property property) throws RepositoryException {
190 String propertyName = property.getName();
191
192
193 if (propertyName.startsWith("jcr:")) {
194 return true;
195 }
196
197
198 if (propertyName.equals(NodeTypes.MGNL_PREFIX + "Data") && property.getString().equals("MetaData")) {
199 return true;
200 }
201
202 return propertyNamesExpectedInMetaDataAfterConversion.contains(propertyName);
203 }
204
205
206
207
208
209
210
211
212 private void moveProperties(Node dstNode, Node srcNode, Map<String, String> nameMappings) throws RepositoryException {
213 for (Map.Entry<String, String> entry : nameMappings.entrySet()) {
214 String srcPropertyName = entry.getKey();
215 String dstPropertyName = entry.getValue();
216 if (!dstNode.hasProperty(dstPropertyName) && srcNode.hasProperty(srcPropertyName)) {
217 moveProperty(srcNode, srcPropertyName, dstNode, dstPropertyName);
218 }
219 }
220 }
221
222
223
224
225
226
227
228
229
230
231
232 private void moveProperty(Node srcNode, String srcPropertyName, Node dstNode, String dstPropertyName) throws RepositoryException {
233 Property srcProperty = srcNode.getProperty(srcPropertyName);
234 if (srcProperty.isMultiple()) {
235 dstNode.setProperty(dstPropertyName, srcProperty.getValues());
236 } else {
237 dstNode.setProperty(dstPropertyName, srcProperty.getValue());
238 }
239 srcProperty.remove();
240 }
241 }