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.ui.form.field.transformer.multi;
35
36 import info.magnolia.cms.i18n.I18nContentSupport;
37 import info.magnolia.jcr.util.NodeTypes;
38 import info.magnolia.jcr.util.NodeUtil;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
41 import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
42 import info.magnolia.ui.form.field.transformer.basic.BasicTransformer;
43 import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeAdapter;
44 import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
45
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Locale;
49
50 import javax.inject.Inject;
51 import javax.jcr.Node;
52 import javax.jcr.RepositoryException;
53
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.vaadin.data.Item;
58 import com.vaadin.data.Property;
59 import com.vaadin.data.util.ObjectProperty;
60 import com.vaadin.data.util.PropertysetItem;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public class DelegatingMultiValueFieldTransformer extends BasicTransformer<PropertysetItem> implements MultiTransformer {
77
78 private static final Logger log = LoggerFactory.getLogger(DelegatingMultiValueFieldTransformer.class);
79
80 protected String childNodeType = NodeTypes.ContentNode.NAME;
81
82 protected String subItemBaseName;
83
84 private List<String> delegateItemNames = new ArrayList<>();
85
86 private PropertysetItem delegateAggregatorItem = new PropertysetItem();
87
88
89
90
91
92
93 @Deprecated
94 public DelegatingMultiValueFieldTransformer(Item relatedFormItem, ConfiguredFieldDefinition definition, Class<PropertysetItem> type, I18nContentSupport i18nContentSupport) {
95 this(relatedFormItem, definition, type, Components.getComponent(I18NAuthoringSupport.class));
96 }
97
98 @Inject
99 public DelegatingMultiValueFieldTransformer(Item relatedFormItem, ConfiguredFieldDefinition definition, Class<PropertysetItem> type, I18NAuthoringSupport i18nAuthoringSupport) {
100 super(relatedFormItem, definition, type, i18nAuthoringSupport);
101 this.subItemBaseName = getSubItemBaseName();
102 }
103
104
105
106
107
108
109
110 @Override
111 public PropertysetItem readFromItem() {
112
113 if (delegateAggregatorItem.getItemPropertyIds().isEmpty()) {
114 JcrNodeAdapter rootItem = getRootItem();
115
116 if (!rootItem.hasChildItemChanges()) {
117 populateStoredChildItems(rootItem);
118 }
119
120 int position = 0;
121 for (String itemName : rootItem.getChildren().keySet()) {
122 if (itemName.matches(childItemRegexRepresentation())) {
123 delegateAggregatorItem.addItemProperty(position, new ObjectProperty<Item>(rootItem.getChild(itemName)));
124 delegateItemNames.add(itemName);
125 ++position;
126 }
127 }
128 }
129 return delegateAggregatorItem;
130 }
131
132
133
134
135 @Override
136 public void writeToItem(PropertysetItem newValue) {
137 log.debug("CALL writeToItem");
138 }
139
140
141
142
143
144
145
146
147 @Override
148 public Property<?> createProperty() {
149 final String newItemName = createNewItemName();
150
151 final JcrNodeAdapter child;
152
153
154 if (getRootItem().getChild(newItemName) instanceof JcrNodeAdapter) {
155 child = (JcrNodeAdapter) getRootItem().getChild(newItemName);
156 } else {
157 child = new JcrNewNodeAdapter(getRootItem().getJcrItem(), childNodeType, newItemName);
158 }
159
160 child.setParent(getRootItem());
161 child.getParent().addChild(child);
162 Property<?> res = new ObjectProperty<Item>(child);
163 delegateAggregatorItem.addItemProperty(delegateAggregatorItem.getItemPropertyIds().size(), res);
164
165 return res;
166 }
167
168 @Override
169 public void removeProperty(Object id) {
170 Property<?> propertyToRemove = delegateAggregatorItem.getItemProperty(id);
171 if (propertyToRemove != null && propertyToRemove.getValue() != null) {
172 JcrNodeAdapter toRemove = (JcrNodeAdapter) propertyToRemove.getValue();
173 toRemove.getParent().removeChild(toRemove);
174 }
175 delegateAggregatorItem.removeItemProperty(id);
176 reorganizeIndex((Integer) id);
177 }
178
179 @Override
180 public void setLocale(Locale locale) {
181 super.setLocale(locale);
182 for (Object id : delegateAggregatorItem.getItemPropertyIds()) {
183 delegateAggregatorItem.removeItemProperty(id);
184 }
185 }
186
187
188
189
190
191
192
193 private void reorganizeIndex(int fromIndex) {
194 int valuesSize = delegateAggregatorItem.getItemPropertyIds().size();
195 if (fromIndex == valuesSize) {
196 return;
197 }
198 while (fromIndex < valuesSize) {
199 int toIndex = fromIndex;
200 ++fromIndex;
201 delegateAggregatorItem.addItemProperty(toIndex, delegateAggregatorItem.getItemProperty(fromIndex));
202 delegateAggregatorItem.removeItemProperty(fromIndex);
203 }
204 }
205
206
207
208
209 protected JcrNodeAdapter getRootItem() {
210 return (JcrNodeAdapter) relatedFormItem;
211 }
212
213
214
215
216
217
218 protected String getSubItemBaseName() {
219 return definition.getName();
220 }
221
222
223
224
225 protected void populateStoredChildItems(JcrNodeAdapter rootItem) {
226 List<Node> childNodes = getStoredChildNodes(rootItem);
227 for (Node child : childNodes) {
228 JcrNodeAdapter item = new JcrNodeAdapter(child);
229 item.setParent(rootItem);
230 item.getParent().addChild(item);
231 }
232 }
233
234
235
236
237 protected List<Node> getStoredChildNodes(JcrNodeAdapter parent) {
238 try {
239 if (!(parent instanceof JcrNewNodeAdapter) && parent.getJcrItem().hasNodes()) {
240 return NodeUtil.asList(NodeUtil.getNodes(parent.getJcrItem(), NodeUtil.MAGNOLIA_FILTER));
241 }
242 } catch (RepositoryException re) {
243 log.warn("Not able to access the Child Nodes of the following Node Identifier {}", parent.getItemId(), re);
244 }
245 return new ArrayList<Node>();
246 }
247
248
249
250
251
252
253
254
255
256
257
258 protected String createNewItemName() {
259 int increment = 0;
260 final List<String> childNodeNames = getChildItemNames();
261 String name;
262 do {
263 name = deriveLocaleAwareName(String.format("%s%d", subItemBaseName, increment));
264 ++increment;
265 } while (childNodeNames.contains(name));
266 return name;
267 }
268
269
270
271
272 protected String childItemRegexRepresentation() {
273 if (hasI18NSupport()) {
274 if (getLocale() == null || getI18NAuthoringSupport().isDefaultLocale(getLocale(), relatedFormItem)) {
275
276
277 return subItemBaseName + incrementRegexRepresentation() + "((?!(_\\w{2}){1,3}))$";
278 } else {
279
280 return getI18NAuthoringSupport().deriveLocalisedPropertyName(subItemBaseName + incrementRegexRepresentation(), getLocale());
281 }
282 } else {
283 return subItemBaseName + incrementRegexRepresentation();
284 }
285 }
286
287 protected String incrementRegexRepresentation() {
288 return "(\\d{1,3})";
289 }
290
291 private List<String> getChildItemNames() {
292 List<String> res = new ArrayList<>();
293 res.addAll(delegateItemNames);
294 for (Object delegateIds : delegateAggregatorItem.getItemPropertyIds()) {
295 Object value = delegateAggregatorItem.getItemProperty(delegateIds).getValue();
296 if (value instanceof JcrNodeAdapter) {
297 res.add(((JcrNodeAdapter) value).getNodeName());
298 }
299 }
300 return res;
301 }
302
303
304
305 @Override
306 public String getBasePropertyName() {
307 return subItemBaseName;
308 }
309
310 @Override
311 public void setI18NPropertyName(String i18NSubNodeName) {
312 log.warn("DelegatingMultiValueFieldTransformer.setI18NPropertyName is deprecated since 5.4.2 - should you need a different locale-specific node name, it is possible to alter #i18nSuffix field in #setLocale() method.");
313 }
314 }