001/* 002 * Copyright 2020 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.content.version; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Set; 022 023import org.apache.cocoon.ProcessingException; 024 025import org.ametys.runtime.i18n.I18nizableText; 026import org.ametys.runtime.model.AbstractViewItemGroup; 027import org.ametys.runtime.model.DefinitionContext; 028import org.ametys.runtime.model.ElementDefinition; 029import org.ametys.runtime.model.ModelViewItemGroup; 030import org.ametys.runtime.model.SimpleViewItemGroup; 031import org.ametys.runtime.model.View; 032import org.ametys.runtime.model.ViewElement; 033import org.ametys.runtime.model.ViewItem; 034import org.ametys.runtime.model.ViewItemContainer; 035import org.ametys.runtime.model.ViewItemGroup; 036 037// Wrap another view, to be able to wrap its own ViewElement to modify the ViewElement#toJson 038class CompareView implements ViewItemContainer 039{ 040 private final View _wrapped; 041 private final Set<String> _externalAndLocalMetadata; 042 043 CompareView(View fromView, Set<String> externalAndLocalMetadata) 044 { 045 _wrapped = new View(); 046 _populateViewItemContainerWithWrappedElements(fromView, _wrapped); 047 _externalAndLocalMetadata = externalAndLocalMetadata; 048 } 049 050 private void _populateViewItemContainerWithWrappedElements(ViewItemContainer sourceOfCopy, ViewItemContainer toPopulate) 051 { 052 for (ViewItem viewItem : sourceOfCopy.getViewItems()) 053 { 054 toPopulate.addViewItem(_wrapViewElement(viewItem)); 055 } 056 } 057 058 private ViewItem _wrapViewElement(ViewItem item) 059 { 060 if (item instanceof ViewElement) 061 { 062 return new CompareViewElement((ViewElement) item); 063 } 064 else if (item instanceof ModelViewItemGroup) 065 { 066 return new CompareModelViewItemGroup((ModelViewItemGroup) item); 067 } 068 else if (item instanceof SimpleViewItemGroup) 069 { 070 return new CompareSimpleViewItemGroup((SimpleViewItemGroup) item); 071 } 072 else 073 { 074 throw new IllegalArgumentException("Unexpected and unhandled ViewItem class '" + item.getClass() + "' to wrap it into a CompareView. Please contact your administrator."); 075 } 076 } 077 078 @Override 079 public List<ViewItem> getViewItems() 080 { 081 return _wrapped.getViewItems(); 082 } 083 084 @Override 085 public void addViewItem(ViewItem item) 086 { 087 throw new UnsupportedOperationException("Should not be called"); 088 } 089 090 @Override 091 public void insertViewItem(ViewItem item, int index) 092 { 093 throw new UnsupportedOperationException("Should not be called"); 094 } 095 096 public boolean removeViewItem(ViewItem item) 097 { 098 throw new UnsupportedOperationException("Should not be called"); 099 } 100 101 public void clear() 102 { 103 throw new UnsupportedOperationException("Should not be called"); 104 } 105 106 Map<String, Object> toJSON() throws ProcessingException 107 { 108 return _wrapped.toJSON(DefinitionContext.newInstance()); 109 } 110 111 // Wrap another group, to be able to wrap its own ViewElement to modify the ViewElement#toJson 112 private abstract static class AbstractCompareViewItemGroup implements ViewItemGroup 113 { 114 protected AbstractViewItemGroup _wrapped; 115 116 @Override 117 public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException 118 { 119 return _wrapped.toJSON(context); 120 } 121 122 @Override 123 public String getName() 124 { 125 return _wrapped.getName(); 126 } 127 128 public I18nizableText getLabel() 129 { 130 return _wrapped.getLabel(); 131 } 132 133 public I18nizableText getDescription() 134 { 135 return _wrapped.getDescription(); 136 } 137 138 @Override 139 public List<ViewItem> getViewItems() 140 { 141 return _wrapped.getViewItems(); 142 } 143 144 public void setName(String name) 145 { 146 throw new UnsupportedOperationException("Should not be called"); 147 } 148 149 public void setLabel(I18nizableText label) 150 { 151 throw new UnsupportedOperationException("Should not be called"); 152 } 153 154 public void setDescription(I18nizableText description) 155 { 156 throw new UnsupportedOperationException("Should not be called"); 157 } 158 159 @Override 160 public void addViewItem(ViewItem item) 161 { 162 throw new UnsupportedOperationException("Should not be called"); 163 } 164 165 @Override 166 public void insertViewItem(ViewItem item, int index) 167 { 168 throw new UnsupportedOperationException("Should not be called"); 169 } 170 171 public boolean removeViewItem(ViewItem item) 172 { 173 throw new UnsupportedOperationException("Should not be called"); 174 } 175 176 public void clear() 177 { 178 throw new UnsupportedOperationException("Should not be called"); 179 } 180 181 @Override 182 public void copyTo(ViewItem item) 183 { 184 throw new UnsupportedOperationException("Should not be called"); 185 } 186 187 @Override 188 public ViewItem createInstance() 189 { 190 throw new UnsupportedOperationException("Should not be called"); 191 } 192 193 public boolean equals(Object obj, boolean checkDetails) 194 { 195 throw new UnsupportedOperationException("Should not be called"); 196 } 197 198 @Override 199 public String getRole() 200 { 201 return _wrapped.getRole(); 202 } 203 204 @Override 205 public void setRole(String role) 206 { 207 _wrapped.setRole(role); 208 } 209 } 210 211 private class CompareModelViewItemGroup extends AbstractCompareViewItemGroup 212 { 213 CompareModelViewItemGroup(ModelViewItemGroup fromGroup) 214 { 215 var wrapped = new ModelViewItemGroup(); // empty constructor and not ModelViewItemGroup.of(fromGroup.getDefinition()), in order to avoid duplication of view items 216 wrapped.setDefinition(fromGroup.getDefinition()); 217 _wrapped = wrapped; 218 _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped); 219 } 220 } 221 222 private class CompareSimpleViewItemGroup extends AbstractCompareViewItemGroup 223 { 224 CompareSimpleViewItemGroup(SimpleViewItemGroup fromGroup) 225 { 226 var wrapped = new SimpleViewItemGroup(); 227 wrapped.setName(fromGroup.getName()); 228 wrapped.setLabel(fromGroup.getLabel()); 229 wrapped.setDescription(fromGroup.getDescription()); 230 wrapped.setRole(fromGroup.getRole()); 231 _wrapped = wrapped; 232 _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped); 233 } 234 } 235 236 // Extends ViewElement, so as to modify the ElementDefinition#toJson 237 private class CompareViewElement extends ViewElement 238 { 239 CompareViewElement(ViewElement wrapped) 240 { 241 @SuppressWarnings("unchecked") 242 CompareElementDefinition compareElementDefinition = new CompareElementDefinition(wrapped.getDefinition()); 243 setDefinition(compareElementDefinition); 244 } 245 } 246 247 // here we are able to modify the ElementDefinition#toJson as we want 248 private class CompareElementDefinition<T> extends ElementDefinition<T> 249 { 250 CompareElementDefinition(ElementDefinition<T> definitionToCopy) 251 { 252 super(definitionToCopy); 253 } 254 255 /* 256 * Here is the business logic for what we created all those wrappers 257 */ 258 @Override 259 protected Map<String, Object> _toJSON(DefinitionContext context) throws ProcessingException 260 { 261 Map<String, Object> elementDefinitionJson = new HashMap<>(super._toJSON(context)); 262 263 @SuppressWarnings("unchecked") 264 Map<String, I18nizableText> widgetParams = (Map<String, I18nizableText>) elementDefinitionJson.computeIfAbsent("widget-params", __ -> new HashMap<>()); 265 String widgetToWrap = (String) elementDefinitionJson.get("widget"); 266 widgetParams.put("comparator-wrapped-widget", new I18nizableText(widgetToWrap)); 267 elementDefinitionJson.put("widget", "edition.comparison"); 268 _handleExternal(widgetToWrap, widgetParams); 269 270 return elementDefinitionJson; 271 } 272 273 private void _handleExternal(String widgetToWrap, Map<String, I18nizableText> widgetParams) 274 { 275 if (_externalAndLocalMetadata.contains(getPath())) 276 { 277 // Wrap the widget as in org.ametys.cms.contenttype.AttributeDefinition#toJSON 278 // Should be more generic 279 widgetParams.put("comparator-wrapped-widget", new I18nizableText("edition.externalizable")); 280 widgetParams.put("wrapped-widget", new I18nizableText(widgetToWrap)); // wrapped widget for externalizable (so, it is wrapped twice) 281 } 282 } 283 } 284}