001/*
002 *  Copyright 2017 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.plugins.extraction.execution;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
031import org.apache.avalon.framework.logger.AbstractLogEnabled;
032import org.apache.avalon.framework.service.ServiceException;
033import org.apache.avalon.framework.service.ServiceManager;
034import org.apache.avalon.framework.service.Serviceable;
035import org.apache.cocoon.components.LifecycleHelper;
036import org.apache.commons.lang3.StringUtils;
037
038import org.ametys.plugins.core.user.UserHelper;
039import org.ametys.plugins.extraction.ExtractionConstants;
040import org.ametys.plugins.extraction.component.CountExtractionComponent;
041import org.ametys.plugins.extraction.component.ExtractionComponent;
042import org.ametys.plugins.extraction.component.MappingQueryExtractionComponent;
043import org.ametys.plugins.extraction.component.QueryExtractionComponent;
044import org.ametys.plugins.extraction.component.ThesaurusExtractionComponent;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.AmetysRepositoryException;
047
048/**
049 * This class reads the extraction definition file
050 */
051public class ExtractionDefinitionReader extends AbstractLogEnabled implements Component, Contextualizable, Serviceable
052{
053    /** The component role. */
054    public static final String ROLE = ExtractionDefinitionReader.class.getName();
055    
056    private Context _context;
057    private ServiceManager _serviceManager;
058    private UserHelper _userHelper;
059    private AmetysObjectResolver _ametysResolver;
060    
061    public void contextualize(Context context) throws ContextException
062    {
063        _context = context;
064    }
065    
066    public void service(ServiceManager manager) throws ServiceException
067    {
068        _serviceManager = manager;
069        _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE);
070        _ametysResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
071    }
072    
073    /**
074     * Read the extraction definition file
075     * @param file extraction definition file
076     * @return the extraction components parsed in the definition file
077     * @throws Exception if an error occurs
078     */
079    public Extraction readExtractionDefinitionFile(File file) throws Exception
080    {
081        long startTime = -1;
082        if (getLogger().isDebugEnabled())
083        {
084            startTime = System.currentTimeMillis();
085            getLogger().debug("Reading definition file");
086        }
087        
088        assert file != null;
089        Configuration configuration = new DefaultConfigurationBuilder().buildFromFile(file);
090        
091        Extraction extraction = new Extraction();
092        
093        _readExtractionDescription(configuration, extraction);
094        _readExtractionAuthor(configuration, extraction);
095        _readVariablesDefinition(configuration, extraction);
096        _readExtractionDefinitionFile(configuration, extraction);
097        
098        if (getLogger().isDebugEnabled())
099        {
100            long endTime = System.currentTimeMillis();
101            getLogger().debug("Read definition file in " + (endTime - startTime) + "ms");
102        }
103        
104        return extraction;
105    }
106    
107    /**
108     * Read the extraction definition file
109     * @param file extraction definition file
110     * @return the extraction components parsed in the definition file
111     * @throws Exception if an error occurs
112     */
113    public Extraction readVariablesDefinitionsInExtractionDefinitionFile(File file) throws Exception
114    {
115        assert file != null;
116        
117        Configuration configuration = new DefaultConfigurationBuilder().buildFromFile(file);
118        Extraction extraction = new Extraction();
119        _readVariablesDefinition(configuration, extraction);
120        
121        return extraction;
122    }
123    
124    private void _readExtractionDescription(Configuration configuration, Extraction extraction)
125    {
126        String descriptionId = configuration.getChild(ExtractionConstants.DESCRIPTION_TAG, true)
127                .getAttribute(ExtractionConstants.DESCRIPTION_IDENTIFIER_ATTRIBUTE_NAME, null);
128        
129        if (StringUtils.isNotBlank(descriptionId))
130        {
131            try
132            {
133                _ametysResolver.resolveById(descriptionId);
134                extraction.setDescriptionId(descriptionId);
135            }
136            catch (AmetysRepositoryException e)
137            {
138                if (getLogger().isWarnEnabled())
139                {
140                    getLogger().warn("Invalid extraction description " + descriptionId + " for configuration " + configuration.getLocation(), e);
141                }
142            }
143        }
144    }
145    
146    private void _readExtractionAuthor(Configuration configuration, Extraction extraction) throws ConfigurationException
147    {
148        Configuration author = configuration.getChild(ExtractionConstants.AUTHOR_TAG, false);
149        if (author != null)
150        {
151            extraction.setAuthor(_userHelper.xml2userIdentity(author));
152        }
153    }
154    
155    private void _readVariablesDefinition(Configuration configuration, Extraction extraction) throws ConfigurationException
156    {
157        for (Configuration child : configuration.getChildren())
158        {
159            switch (child.getName())
160            {
161                case ExtractionConstants.OPTIONAL_COLUMNS_TAG:
162                    extraction.setDisplayOptionalColumnsNames(getDisplayOptionalColumnNames(child));
163                    break;
164                case ExtractionConstants.CLAUSES_VARIABLES_TAG:
165                    extraction.setQueryVariablesNamesAndContentTypes(getQueryVariablesNamesAndContentTypes(child));
166                    break;
167                default:
168                    // Do nothing, we only check variables definitions
169            }
170        }
171    }
172    
173    private void _readExtractionDefinitionFile(Configuration configuration, Extraction extraction) throws Exception
174    {
175        for (Configuration child : configuration.getChildren())
176        {
177            switch (child.getName())
178            {
179                case ExtractionConstants.QUERY_COMPONENT_TAG:
180                case ExtractionConstants.THESAURUS_COMPONENT_TAG:
181                case ExtractionConstants.COUNT_COMPONENT_TAG:
182                case ExtractionConstants.MAPPING_QUERY_COMPONENT_TAG:
183                    ExtractionComponent component = _processExtractionComponent(child);
184                    extraction.addExtractionComponent(component);
185                    break;
186                default:
187                    // Do nothing
188            }
189        }
190    }
191
192    private List<String> getDisplayOptionalColumnNames(Configuration configuration) throws ConfigurationException
193    {
194        List<String> names = new ArrayList<>();
195        for (Configuration nameConfiguration : configuration.getChildren("name"))
196        {
197            names.add(nameConfiguration.getValue());
198        }
199        return names;
200    }
201    
202    private Map<String, String> getQueryVariablesNamesAndContentTypes(Configuration configuration)
203    {
204        Map<String, String> variables = new HashMap<>();
205        for (Configuration variable : configuration.getChildren("variable"))
206        {
207            String name = variable.getAttribute("name", null);
208            String contentType = variable.getAttribute("contentType", null);
209            if (null == name || null == contentType)
210            {
211                throw new IllegalArgumentException("Query variables are not well defined. A query variable is defined by a name and a content type.");
212            }
213            variables.put(name, contentType);
214        }
215        return variables;
216    }
217
218    private ExtractionComponent _processExtractionComponent(Configuration componentConfiguration) throws Exception
219    {
220        ExtractionComponent component = null;
221        switch (componentConfiguration.getName())
222        {
223            case "query":
224                component = new QueryExtractionComponent();
225                break;
226            case "count":
227                component = new CountExtractionComponent();
228                break;
229            case "thesaurus":
230                component = new ThesaurusExtractionComponent();
231                break;
232            case "mapping-query":
233                component = new MappingQueryExtractionComponent();
234                break;
235            default:
236                // do nothing
237                break;
238        }
239
240        if (component != null)
241        {
242            LifecycleHelper.setupComponent(component, getLogger(), _context, _serviceManager, componentConfiguration);
243            for (Configuration child : componentConfiguration.getChildren())
244            {
245                ExtractionComponent subComponent = _processExtractionComponent(child);
246                if (null != subComponent)
247                {
248                    component.addSubComponent(subComponent);
249                }
250            }
251        }
252        return component;
253    }
254}