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.contenttype;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Optional;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.context.Context;
026import org.apache.avalon.framework.context.ContextException;
027import org.apache.avalon.framework.context.Contextualizable;
028import org.apache.avalon.framework.logger.AbstractLogEnabled;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.cocoon.components.LifecycleHelper;
033import org.apache.commons.lang3.StringUtils;
034
035import org.ametys.cms.contenttype.ContentTypesHelper.ViewConfigurationsByType;
036import org.ametys.runtime.i18n.I18nizableText;
037import org.ametys.runtime.model.ModelHelper.ConfigurationAndPluginName;
038import org.ametys.runtime.model.View;
039import org.ametys.runtime.model.ViewParser;
040
041/**
042 * Helper for content types parsing
043 */
044public class ContentTypesParserHelper extends AbstractLogEnabled implements Component, Serviceable, Contextualizable
045{
046    /** The Avalon role */
047    public static final String ROLE = ContentTypesParserHelper.class.getName();
048    
049    /** The content types helper */
050    protected ContentTypesHelper _contentTypesHelper;
051
052    private Context _context;
053    private ServiceManager _manager;
054    
055
056    public void contextualize(Context context) throws ContextException
057    {
058        _context = context;
059    }
060    
061    @Override
062    public void service(ServiceManager manager) throws ServiceException
063    {
064        _manager = manager;
065        _contentTypesHelper = (ContentTypesHelper) _manager.lookup(ContentTypesHelper.ROLE);
066    }
067    
068    /**
069     * Parses the view of the given content type from the configurations
070     * @param contentType the content type
071     * @param viewConfigurationsByType the view's configurations, indexed by the content types defining this view
072     * @return the parsed view
073     * @throws ConfigurationException if the configuration is invalid
074     */
075    public View parseView(ContentType contentType, ViewConfigurationsByType viewConfigurationsByType) throws ConfigurationException
076    {
077        // Check that there is at least one main configuration
078        if (viewConfigurationsByType.mainConfigurations().isEmpty())
079        {
080            throw new ConfigurationException ("There is no main configuration but only overrides for view '" + viewConfigurationsByType.viewName() + "' in content type '" + contentType.getId() + "'");
081        }
082        
083        // Parse View from main configurations
084        List<View> views = new ArrayList<>();
085        for (ContentType originalContentType : viewConfigurationsByType.mainConfigurations().keySet())
086        {
087            View view = _parseView(contentType, originalContentType, viewConfigurationsByType.mainConfigurations().get(originalContentType));
088            views.add(view);
089        }
090        View view = _contentTypesHelper.joinViews(views);
091        
092        // Parse overrides
093        for (ContentType originalContentType : viewConfigurationsByType.overrides().keySet())
094        {
095            for (ConfigurationAndPluginName configuration : viewConfigurationsByType.overrides().get(originalContentType))
096            {
097                view = _overrideView(contentType, originalContentType, configuration, view);
098            }
099        }
100        
101        return view;
102    }
103    
104    private View _parseView(ContentType contentType, ContentType originalContentType, ConfigurationAndPluginName viewConfiguration) throws ConfigurationException
105    {
106        ViewParser parser = _getContentTypeViewParser(contentType, originalContentType, viewConfiguration.configuration());
107        try
108        {
109            LifecycleHelper.setupComponent(parser, getLogger(), _context, _manager, null);
110            return parser.parseView(viewConfiguration);
111        }
112        catch (Exception e)
113        {
114            throw new ConfigurationException("Unable to initialize the content view parser", viewConfiguration.configuration(), e);
115        }
116        finally
117        {
118            LifecycleHelper.dispose(parser);
119        }
120    }
121    
122    private View _overrideView(ContentType contentType, ContentType originalContentType, ConfigurationAndPluginName viewConfiguration, View existingView) throws ConfigurationException
123    {
124        ViewParser parser = _getContentTypeViewParser(contentType, originalContentType, viewConfiguration.configuration());
125        try
126        {
127            LifecycleHelper.setupComponent(parser, getLogger(), _context, _manager, null);
128            return parser.overrideView(viewConfiguration, existingView);
129        }
130        catch (Exception e)
131        {
132            throw new ConfigurationException("Unable to initialize the content view parser", viewConfiguration.configuration(), e);
133        }
134        finally
135        {
136            LifecycleHelper.dispose(parser);
137        }
138    }
139    
140    private ViewParser _getContentTypeViewParser(ContentType contentType, ContentType originalContentType, Configuration viewConfiguration)
141    {
142        return ContentType.VIEW_TAG_NAME.equals(viewConfiguration.getName())
143                ? new ContentTypeViewParser(contentType, originalContentType)
144                : new ContentTypeViewParser(contentType, originalContentType, Optional.of(ContentType.GROUP_TAG_NAME_WITH_LEGACY_SYNTAX), Optional.of(ContentType.ATTRIBUTE_REF_TAG_NAME_WITH_LEGACY_SYNTAX), Optional.empty(), false);
145    }
146
147    /**
148     * Parse an i18n text.
149     * @param configurationAndPluginName the configuration to use.
150     * @param name the child name.
151     * @return the i18n text.
152     * @throws ConfigurationException if the configuration is not valid.
153     */
154    public I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name) throws ConfigurationException
155    {
156        return parseI18nizableText(configurationAndPluginName, name, StringUtils.EMPTY);
157    }
158    
159    /**
160     * Parse an i18n text.
161     * @param configurationAndPluginName the configuration to use.
162     * @param name the child name.
163     * @param defaultValue the default value if no present
164     * @return the i18n text.
165     * @throws ConfigurationException if the configuration is not valid.
166     */
167    public I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name, String defaultValue) throws ConfigurationException
168    {
169        return I18nizableText.parseI18nizableText(configurationAndPluginName.configuration().getChild(name), "plugin." + configurationAndPluginName.pluginName(), defaultValue);
170    }
171    
172    /**
173     * Parse an i18n text.
174     * @param configurationAndPluginName the configuration to use.
175     * @param name the child name.
176     * @param defaultValue the default value if no present
177     * @return the i18n text.
178     * @throws ConfigurationException if the configuration is not valid.
179     */
180    public I18nizableText parseI18nizableText(ConfigurationAndPluginName configurationAndPluginName, String name, I18nizableText defaultValue) throws ConfigurationException
181    {
182        return I18nizableText.parseI18nizableText(configurationAndPluginName.configuration().getChild(name), "plugin." + configurationAndPluginName.pluginName(), defaultValue);
183    }
184    
185    
186    /**
187     * Parse an icon path
188     * @param contentType the content type
189     * @param configuration the configuration to use
190     * @param name the child name.
191     * @return The icon path
192     * @throws ConfigurationException if the configuration is not valid.
193     */
194    public String parseIcon (ContentTypeDescriptor contentType, Configuration configuration, String name) throws ConfigurationException
195    {
196        return parseIcon(contentType, configuration, name, "/plugins/cms/resources/img/contenttype/unknown-" + name + ".png");
197    }
198    
199    /**
200     * Parse an icon path
201     * @param contentType the content type
202     * @param configuration the configuration to use
203     * @param name the child name.
204     * @param defaultValue the default value.
205     * @return The icon path
206     * @throws ConfigurationException if the configuration is not valid.
207     */
208    public String parseIcon(ContentTypeDescriptor contentType, Configuration configuration, String name, String defaultValue) throws ConfigurationException
209    {
210        Configuration iconConfig = configuration.getChild(name, false);
211        if (iconConfig != null)
212        {
213            String pluginName = iconConfig.getAttribute("plugin", contentType.getPluginName());
214            return contentType.getIconPath(pluginName) + iconConfig.getValue();
215        }
216        
217        return defaultValue;
218    }
219    
220    /**
221     * Retrieves the default value for the view's icon glyph.
222     * For all views, use medium icon or a default one.
223     * For some common views (main, abstract, link), a specific default icon is used
224     * @param viewName the name of the view
225     * @param mediumIcon the medium icon of the view
226     * @return the default value for the view's icon glyph. 
227     */
228    public String getIconGlyphDefaultValue(String viewName, String mediumIcon)
229    {
230        String defaultValue = StringUtils.defaultString(mediumIcon, "ametysicon-column3");
231        switch (viewName)
232        {
233            case "main":
234                defaultValue = "ametysicon-document112";
235                break;
236            case "abstract":
237                defaultValue = "ametysicon-document77";
238                break;
239            case "link":
240                defaultValue = "ametysicon-internet58";
241                break;
242            default:
243                break;
244        }
245        
246        return defaultValue;
247    }
248}