View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.index.service.catalog.adapter;
22  
23  import static org.opencastproject.index.service.catalog.adapter.CatalogUIAdapterFactory.CONF_FLAVOR_KEY;
24  import static org.opencastproject.index.service.catalog.adapter.CatalogUIAdapterFactory.CONF_ORGANIZATION_KEY;
25  import static org.opencastproject.index.service.catalog.adapter.CatalogUIAdapterFactory.CONF_TITLE_KEY;
26  import static org.opencastproject.util.OsgiUtil.getCfg;
27  
28  import org.opencastproject.list.api.ListProviderException;
29  import org.opencastproject.list.api.ListProvidersService;
30  import org.opencastproject.list.api.ResourceListQuery;
31  import org.opencastproject.mediapackage.EName;
32  import org.opencastproject.mediapackage.MediaPackageElementFlavor;
33  import org.opencastproject.metadata.dublincore.CatalogUIAdapter;
34  import org.opencastproject.metadata.dublincore.DublinCore;
35  import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
36  import org.opencastproject.metadata.dublincore.DublinCoreMetadataCollection;
37  import org.opencastproject.metadata.dublincore.DublinCoreValue;
38  import org.opencastproject.metadata.dublincore.MetadataField;
39  import org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter;
40  
41  import org.osgi.service.cm.ConfigurationException;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  import java.util.ArrayList;
46  import java.util.Collections;
47  import java.util.Dictionary;
48  import java.util.HashMap;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.Optional;
52  import java.util.stream.Collectors;
53  
54  public abstract class ConfigurableDCCatalogUIAdapter implements CatalogUIAdapter {
55  
56    private static final Logger logger = LoggerFactory.getLogger(ConfigurableDCCatalogUIAdapter.class);
57  
58    /** The catalog UI adapter configuration */
59    protected CatalogUIAdapterConfiguration config;
60  
61    /** The organization name */
62    protected String organization;
63  
64    /** The flavor of this catalog */
65    protected MediaPackageElementFlavor flavor;
66  
67    /** The title of this catalog */
68    protected String title;
69  
70    /** The metadata fields for all properties of the underlying DublinCore */
71    protected Map<String, MetadataField> dublinCoreProperties;
72  
73    /** Reference to the list providers service */
74    protected ListProvidersService listProvidersService;
75  
76    /**
77     * Reconfigures the {@link SeriesCatalogUIAdapter} instance with an updated set of configuration properties;
78     *
79     * @param properties
80     *          the configuration properties
81     * @throws ConfigurationException
82     *           if there is a configuration error
83     */
84    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
85      config = CatalogUIAdapterConfiguration.loadFromDictionary(properties);
86      organization = getCfg(properties, CONF_ORGANIZATION_KEY);
87      flavor = MediaPackageElementFlavor.parseFlavor(getCfg(properties, CONF_FLAVOR_KEY));
88      title = getCfg(properties, CONF_TITLE_KEY);
89      dublinCoreProperties = DublinCoreMetadataUtil.getDublinCoreProperties(properties);
90    }
91  
92    /**
93     * Get default value for collection from list providers service
94     * @param metadataField
95     * @param listProvidersService
96     * @return default value
97     */
98    private String getCollectionDefault(MetadataField metadataField,
99            ListProvidersService listProvidersService) {
100     if (listProvidersService != null && metadataField.getListprovider() != null) {
101       try {
102         return listProvidersService.getDefault(metadataField.getListprovider());
103 
104       } catch (ListProviderException ex) {
105         // failed to get default property on list-provider-service
106         // as this field is optional, it is fine to pass here
107       }
108     }
109     return null;
110   }
111 
112   @Override
113   public DublinCoreMetadataCollection getRawFields() {
114     return getRawFields(Collections.emptyMap());
115   }
116 
117   @Override
118   public DublinCoreMetadataCollection getRawFields(Map<String, ResourceListQuery> collectionQueryOverrides) {
119     DublinCoreMetadataCollection rawFields = new DublinCoreMetadataCollection();
120     for (MetadataField metadataField : dublinCoreProperties.values()) {
121       try {
122         String defaultKey = getCollectionDefault(metadataField, listProvidersService);
123         ResourceListQuery collectionQueryOverride = collectionQueryOverrides.get(metadataField.getOutputID());
124 
125         rawFields.addField(new MetadataField(metadataField), Optional.ofNullable(defaultKey),
126                 Optional.ofNullable(collectionQueryOverride), listProvidersService);
127       } catch (IllegalArgumentException e) {
128         logger.error("Skipping metadata field '{}' because of error", metadataField, e);
129       }
130     }
131     return rawFields;
132   }
133 
134   @Override
135   public DublinCoreMetadataCollection getRawFields(ResourceListQuery collectionQueryOverride) {
136     DublinCoreMetadataCollection rawFields = new DublinCoreMetadataCollection();
137     for (MetadataField metadataField : dublinCoreProperties.values()) {
138       try {
139         String defaultKey = getCollectionDefault(metadataField, listProvidersService);
140 
141         rawFields.addField(new MetadataField(metadataField), Optional.ofNullable(defaultKey),
142             Optional.ofNullable(collectionQueryOverride), listProvidersService);
143       } catch (IllegalArgumentException e) {
144         logger.error("Skipping metadata field '{}' because of error", metadataField, e);
145       }
146     }
147     return rawFields;
148   }
149 
150   protected DublinCoreMetadataCollection getFieldsFromCatalogs(List<DublinCoreCatalog> dcCatalogs) {
151     Map<String,List<MetadataField>> metadataFields = new HashMap<>();
152     List<MetadataField> emptyFields = new ArrayList<>(dublinCoreProperties.values());
153 
154     for (MetadataField metadataField: dublinCoreProperties.values()) {
155 
156       String namespace = DublinCore.TERMS_NS_URI;
157       if (metadataField.getNamespace() != null) {
158         namespace = metadataField.getNamespace();
159       }
160 
161       String metadataFieldKey = namespace.toLowerCase() + ":" + metadataField.getInputID().toLowerCase();
162 
163       List<MetadataField> metadataFieldList = metadataFields.computeIfAbsent(metadataFieldKey,
164               key -> new ArrayList<>());
165       metadataFieldList.add(metadataField);
166     }
167 
168     DublinCoreMetadataCollection dublinCoreMetadata = new DublinCoreMetadataCollection();
169     for (DublinCoreCatalog dc : dcCatalogs) {
170       getFieldsFromCatalog(metadataFields, emptyFields, dublinCoreMetadata, dc);
171     }
172 
173     // Add all of the rest of the fields that didn't have values as empty.
174     for (MetadataField metadataField: emptyFields) {
175       try {
176         dublinCoreMetadata.addEmptyField(new MetadataField(metadataField), getListProvidersService());
177       } catch (IllegalArgumentException e) {
178         logger.error("Skipping metadata field '{}' because of error", metadataField, e);
179       }
180     }
181     return dublinCoreMetadata;
182   }
183 
184   private void getFieldsFromCatalog(
185           Map<String, List<MetadataField>> metadataFields,
186           List<MetadataField> emptyFields,
187           DublinCoreMetadataCollection dublinCoreMetadata,
188           DublinCoreCatalog dc) {
189     for (EName propertyKey : dc.getValues().keySet()) {
190       // namespace and input id need to match
191       final String metadataFieldKey = propertyKey.getNamespaceURI().toLowerCase() + ":"
192               + propertyKey.getLocalName().toLowerCase();
193       if (metadataFields.containsKey(metadataFieldKey)) {
194 
195         // multiple metadata fields can match
196         for (MetadataField metadataField : metadataFields.get(metadataFieldKey)) {
197           List<DublinCoreValue> values = dc.get(propertyKey);
198           if (!values.isEmpty()) {
199             try {
200               dublinCoreMetadata.addField(
201                       new MetadataField(metadataField),
202                       values.stream().map(DublinCoreValue::getValue).collect(Collectors.toList()),
203                       getListProvidersService());
204               emptyFields.remove(metadataField);
205             } catch (IllegalArgumentException e) {
206               logger.error("Skipping metadata field '{}' because of error:", metadataField.getInputID(), e);
207             }
208           }
209         }
210       }
211     }
212   }
213 
214   @Override
215   public String getOrganization() {
216     return organization;
217   }
218 
219   @Override
220   public boolean handlesOrganization(String organization) {
221     return ORGANIZATION_WILDCARD.equals(this.organization) || organization.equals(this.organization);
222   }
223 
224   @Override
225   public MediaPackageElementFlavor getFlavor() {
226     return flavor;
227   }
228 
229   @Override
230   public String getUITitle() {
231     return title;
232   }
233 
234   public void setListProvidersService(ListProvidersService listProvidersService) {
235     this.listProvidersService = listProvidersService;
236   }
237 
238   protected ListProvidersService getListProvidersService() {
239     return listProvidersService;
240   }
241 }