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; 024import org.apache.commons.lang3.NotImplementedException; 025 026import org.ametys.runtime.i18n.I18nizableText; 027import org.ametys.runtime.model.AbstractViewItemGroup; 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 NotImplementedException("Should not be called"); 088 } 089 090 @Override 091 public void insertViewItem(ViewItem item, int index) 092 { 093 throw new NotImplementedException("Should not be called"); 094 } 095 096 Map<String, Object> toJSON() throws ProcessingException 097 { 098 return _wrapped.toJSON(); 099 } 100 101 // Wrap another group, to be able to wrap its own ViewElement to modify the ViewElement#toJson 102 private abstract static class AbstractCompareViewItemGroup implements ViewItemGroup 103 { 104 protected AbstractViewItemGroup _wrapped; 105 106 @Override 107 public Map<String, Object> toJSON() throws ProcessingException 108 { 109 return _wrapped.toJSON(); 110 } 111 112 @Override 113 public String getName() 114 { 115 return _wrapped.getName(); 116 } 117 118 @Override 119 public List<ViewItem> getViewItems() 120 { 121 return _wrapped.getViewItems(); 122 } 123 124 @Override 125 public void addViewItem(ViewItem item) 126 { 127 throw new NotImplementedException("Should not be called"); 128 } 129 130 @Override 131 public void insertViewItem(ViewItem item, int index) 132 { 133 throw new NotImplementedException("Should not be called"); 134 } 135 136 @Override 137 public String getRole() 138 { 139 return _wrapped.getRole(); 140 } 141 142 @Override 143 public void setRole(String role) 144 { 145 _wrapped.setRole(role); 146 } 147 } 148 149 private class CompareModelViewItemGroup extends AbstractCompareViewItemGroup 150 { 151 CompareModelViewItemGroup(ModelViewItemGroup fromGroup) 152 { 153 var wrapped = new ModelViewItemGroup(); // empty constructor and not ModelViewItemGroup.of(fromGroup.getDefinition()), in order to avoid duplication of view items 154 wrapped.setDefinition(fromGroup.getDefinition()); 155 _wrapped = wrapped; 156 _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped); 157 } 158 } 159 160 private class CompareSimpleViewItemGroup extends AbstractCompareViewItemGroup 161 { 162 CompareSimpleViewItemGroup(SimpleViewItemGroup fromGroup) 163 { 164 var wrapped = new SimpleViewItemGroup(); 165 wrapped.setName(fromGroup.getName()); 166 wrapped.setLabel(fromGroup.getLabel()); 167 wrapped.setDescription(fromGroup.getDescription()); 168 wrapped.setRole(fromGroup.getRole()); 169 _wrapped = wrapped; 170 _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped); 171 } 172 } 173 174 // Extends ViewElement, so as to modify the ElementDefinition#toJson 175 private class CompareViewElement extends ViewElement 176 { 177 CompareViewElement(ViewElement wrapped) 178 { 179 @SuppressWarnings("unchecked") 180 CompareElementDefinition compareElementDefinition = new CompareElementDefinition(wrapped.getDefinition()); 181 setDefinition(compareElementDefinition); 182 } 183 } 184 185 // here we are able to modify the ElementDefinition#toJson as we want 186 private class CompareElementDefinition<T> extends ElementDefinition<T> 187 { 188 CompareElementDefinition(ElementDefinition<T> definitionToCopy) 189 { 190 super(definitionToCopy); 191 } 192 193 /* 194 * Here is the business logic for what we created all those wrappers 195 */ 196 @Override 197 public Map<String, Object> toJSON() throws ProcessingException 198 { 199 Map<String, Object> elementDefinitionJson = new HashMap<>(super.toJSON()); 200 201 @SuppressWarnings("unchecked") 202 Map<String, I18nizableText> widgetParams = (Map<String, I18nizableText>) elementDefinitionJson.computeIfAbsent("widget-params", __ -> new HashMap<>()); 203 String widgetToWrap = (String) elementDefinitionJson.get("widget"); 204 widgetParams.put("comparator-wrapped-widget", new I18nizableText(widgetToWrap)); 205 elementDefinitionJson.put("widget", "edition.comparison"); 206 _handleExternal(widgetToWrap, widgetParams); 207 208 // for a reason, type can be 'rich-text' while it expects 'rich_text' 209 elementDefinitionJson.computeIfPresent("type", (__, type) -> ((String) type).replaceAll("-", "_")); 210 211 return elementDefinitionJson; 212 } 213 214 private void _handleExternal(String widgetToWrap, Map<String, I18nizableText> widgetParams) 215 { 216 if (_externalAndLocalMetadata.contains(getPath())) 217 { 218 // Do as in org.ametys.cms.content.GetMetadataSetDefAction.metadataDefinition2JsonObject 219 // Should be more generic 220 widgetParams.put("comparator-wrapped-widget", new I18nizableText("edition.externalizable")); 221 widgetParams.put("wrapped-widget", new I18nizableText(widgetToWrap)); // wrapped widget for externalizable (so, it is wrapped twice) 222 } 223 } 224 } 225}