blob: 58dfc50d57f67aeaa7e9fab35e56cca010a41519 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.metatype.impl;
import java.net.URL;
import java.util.*;
import javax.xml.parsers.SAXParser;
import org.eclipse.equinox.metatype.EquinoxObjectClassDefinition;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.service.log.LogService;
import org.osgi.service.metatype.MetaTypeProvider;
import org.osgi.service.metatype.MetaTypeService;
/**
* Implementation of MetaTypeProvider
*/
public class MetaTypeProviderImpl implements MetaTypeProvider {
public static final String METADATA_NOT_FOUND = "METADATA_NOT_FOUND"; //$NON-NLS-1$
public static final String OCD_ID_NOT_FOUND = "OCD_ID_NOT_FOUND"; //$NON-NLS-1$
public static final String ASK_INVALID_LOCALE = "ASK_INVALID_LOCALE"; //$NON-NLS-1$
public static final String META_FILE_EXT = ".XML"; //$NON-NLS-1$
public static final String RESOURCE_FILE_CONN = "_"; //$NON-NLS-1$
public static final String RESOURCE_FILE_EXT = ".properties"; //$NON-NLS-1$
public static final char DIRECTORY_SEP = '/';
Bundle _bundle;
Hashtable<String, ObjectClassDefinitionImpl> _allPidOCDs = new Hashtable<String, ObjectClassDefinitionImpl>(7);
Hashtable<String, ObjectClassDefinitionImpl> _allFPidOCDs = new Hashtable<String, ObjectClassDefinitionImpl>(7);
String[] _locales;
boolean _isThereMeta = false;
// Give access to subclasses.
protected final LogTracker logger;
/**
* Constructor of class MetaTypeProviderImpl.
*/
MetaTypeProviderImpl(Bundle bundle, SAXParser parser, LogTracker logger) {
this._bundle = bundle;
this.logger = logger;
// read all bundle's metadata files and build internal data structures
_isThereMeta = readMetaFiles(bundle, parser);
if (!_isThereMeta) {
logger.log(LogService.LOG_DEBUG, NLS.bind(MetaTypeMsg.METADATA_NOT_FOUND, bundle.getSymbolicName(), bundle.getBundleId()));
}
}
/**
* This method should do the following:
* <p> - Obtain a SAX parser from the XML Parser Service:
* <p>
*
* <pre> </pre>
*
* The parser may be SAX 1 (eXML) or SAX 2 (XML4J). It should attempt to use
* a SAX2 parser by instantiating an XMLReader and extending DefaultHandler
* BUT if that fails it should fall back to instantiating a SAX1 Parser and
* extending HandlerBase.
* <p> - Pass the parser the URL for the bundle's METADATA.XML file
* <p> - Handle the callbacks from the parser and build the appropriate
* MetaType objects - ObjectClassDefinitions & AttributeDefinitions
*
* @param bundle The bundle object for which the metadata should be read
* @param parserFactory The bundle object for which the metadata should be
* read
* @return void
*/
private boolean readMetaFiles(Bundle bundle, SAXParser saxParser) {
Enumeration<URL> entries = bundle.findEntries(MetaTypeService.METATYPE_DOCUMENTS_LOCATION, "*", false); //$NON-NLS-1$
if (entries == null)
return false;
boolean result = false;
for (URL entry : Collections.list(entries)) {
if (entry.getPath().endsWith("/")) //$NON-NLS-1$
continue;
DataParser parser = new DataParser(bundle, entry, saxParser, logger);
try {
Collection<Designate> designates = parser.doParse();
if (!designates.isEmpty()) {
result = true;
}
for (Designate designate : designates) {
if (designate.isFactory()) {
_allFPidOCDs.put(designate.getFactoryPid(), designate.getObjectClassDefinition());
} else {
_allPidOCDs.put(designate.getPid(), designate.getObjectClassDefinition());
}
}
} catch (Exception e) {
logger.log(LogService.LOG_WARNING, NLS.bind(MetaTypeMsg.METADATA_FILE_PARSE_ERROR, new Object[] {entry, bundle.getBundleId(), bundle.getSymbolicName()}), e);
}
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.metatype.MetaTypeProvider#getObjectClassDefinition(java.lang.String,
* java.lang.String)
*/
public EquinoxObjectClassDefinition getObjectClassDefinition(String pid, String locale) {
if (isInvalidLocale(locale)) {
throw new IllegalArgumentException(NLS.bind(MetaTypeMsg.ASK_INVALID_LOCALE, pid, locale));
}
ObjectClassDefinitionImpl ocd;
if (_allPidOCDs.containsKey(pid)) {
ocd = (ObjectClassDefinitionImpl) (_allPidOCDs.get(pid)).clone();
ocd.setResourceBundle(locale, _bundle);
return ocd;
} else if (_allFPidOCDs.containsKey(pid)) {
ocd = (ObjectClassDefinitionImpl) (_allFPidOCDs.get(pid)).clone();
ocd.setResourceBundle(locale, _bundle);
return ocd;
} else {
throw new IllegalArgumentException(NLS.bind(MetaTypeMsg.OCD_PID_NOT_FOUND, pid));
}
}
/**
* Internal Method - Check if the locale is invalid.
*/
public boolean isInvalidLocale(String locale) {
// Just a simple and quick check here.
if (locale == null || locale.length() == 0)
return false;
int idx_first = locale.indexOf(LocalizationElement.LOCALE_SEP);
int idx_second = locale.lastIndexOf(LocalizationElement.LOCALE_SEP);
if (idx_first == -1 && locale.length() == 2)
// It is format of only language.
return false;
if ((idx_first == 2) && (idx_second == 5 || idx_second == 2))
// It is format of language + "_" + country [ + "_" + variation ].
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.metatype.MetaTypeProvider#getLocales()
*/
public synchronized String[] getLocales() {
if (_locales != null)
return checkForDefault(_locales);
Vector<String> localizationFiles = new Vector<String>(7);
// get all the localization resources for PIDS
Enumeration<ObjectClassDefinitionImpl> ocds = _allPidOCDs.elements();
while (ocds.hasMoreElements()) {
ObjectClassDefinitionImpl ocd = ocds.nextElement();
String localization = ocd.getLocalization();
if (localization != null && !localizationFiles.contains(localization))
localizationFiles.add(localization);
}
// get all the localization resources for FPIDS
ocds = _allFPidOCDs.elements();
while (ocds.hasMoreElements()) {
ObjectClassDefinitionImpl ocd = ocds.nextElement();
String localization = ocd.getLocalization();
if (localization != null && !localizationFiles.contains(localization))
localizationFiles.add(localization);
}
if (localizationFiles.size() == 0)
localizationFiles.add(getBundleLocalization(_bundle));
Vector<String> locales = new Vector<String>(7);
Enumeration<String> eLocalizationFiles = localizationFiles.elements();
while (eLocalizationFiles.hasMoreElements()) {
String localizationFile = eLocalizationFiles.nextElement();
int iSlash = localizationFile.lastIndexOf(DIRECTORY_SEP);
String baseDir;
String baseFileName;
if (iSlash < 0) {
baseDir = ""; //$NON-NLS-1$
} else {
baseDir = localizationFile.substring(0, iSlash);
}
baseFileName = '/' + localizationFile + RESOURCE_FILE_CONN;
Enumeration<URL> entries = _bundle.findEntries(baseDir, "*.properties", false); //$NON-NLS-1$
if (entries == null)
continue;
while (entries.hasMoreElements()) {
String resource = entries.nextElement().getPath();
if (resource.startsWith(baseFileName) && resource.toLowerCase().endsWith(RESOURCE_FILE_EXT))
locales.add(resource.substring(baseFileName.length(), resource.length() - RESOURCE_FILE_EXT.length()));
}
}
_locales = locales.toArray(new String[locales.size()]);
return checkForDefault(_locales);
}
static String getBundleLocalization(Bundle bundle) {
// Use the Bundle-Localization manifest header value if it exists.
String baseName = bundle.getHeaders("").get(Constants.BUNDLE_LOCALIZATION); //$NON-NLS-1$
if (baseName == null)
// If the manifest header does not exist, use the default.
baseName = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
return baseName;
}
/**
* Internal Method - checkForDefault
*/
private String[] checkForDefault(String[] locales) {
if (locales == null || locales.length == 0 || (locales.length == 1 && Locale.getDefault().toString().equals(locales[0])))
return null;
return locales;
}
}