blob: 6586c01a838bf66a2c0fe36a3979e56835c5cb9d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
* Jesper Steen Moeller - Added XML Catalogs 1.1 support
*
*******************************************************************************/
package org.eclipse.wst.xml.core.internal.catalog;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Platform;
import org.eclipse.wst.xml.core.internal.Logger;
import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalog;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogElement;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEntry;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEvent;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogListener;
import org.eclipse.wst.xml.core.internal.catalog.provisional.IDelegateCatalog;
import org.eclipse.wst.xml.core.internal.catalog.provisional.INextCatalog;
import org.eclipse.wst.xml.core.internal.catalog.provisional.IRewriteEntry;
import org.eclipse.wst.xml.core.internal.catalog.provisional.ISuffixEntry;
public class Catalog implements ICatalog
{
class CatalogLS
{
public void load()
{
}
public synchronized void save()
{
try
{
new CatalogWriter().write(Catalog.this, location);
} catch (Exception e)
{
Logger.logException(e);
}
}
}
class DefaultCatalogLS extends CatalogLS
{
public void load()
{
NextCatalog userCatalogReference = new NextCatalog();
userCatalogReference.setId(XMLCorePlugin.USER_CATALOG_ID);
userCatalogReference.setCatalogLocation(USER_CATALOG_FILE);
addCatalogElement(userCatalogReference);
NextCatalog systemCatalogReference = new NextCatalog();
systemCatalogReference.setId(XMLCorePlugin.SYSTEM_CATALOG_ID);
systemCatalogReference.setCatalogLocation(SYSTEM_CATALOG_FILE);
addCatalogElement(systemCatalogReference);
/*
* Here we save the file in order to 'reflect' the catalog that
* we've created from plug-in extensions to disk. The 'default'
* catalog is only ever written to disk and never read from disk.
*/
save();
}
}
private static Comparator LONGEST_REWRITE_FIRST = new Comparator()
{
public int compare(Object entry1, Object entry2)
{
String start1 = ((IRewriteEntry)entry1).getStartString();
String start2 = ((IRewriteEntry)entry2).getStartString();
// Bigger is earlier
return start2.length() - start1.length();
}
};
private static Comparator LONGEST_SUFFIX_FIRST = new Comparator()
{
public int compare(Object entry1, Object entry2)
{
String suffix1 = ((ISuffixEntry)entry1).getSuffix();
String suffix2 = ((ISuffixEntry)entry2).getSuffix();
// Bigger is earlier
return suffix2.length() - suffix1.length();
}
};
private static Comparator LONGEST_DELEGATE_PREFIX_FIRST = new Comparator()
{
public int compare(Object entry1, Object entry2)
{
String prefix1 = ((IDelegateCatalog)entry1).getStartString();
String prefix2 = ((IDelegateCatalog)entry2).getStartString();
// Bigger is earlier
return prefix2.length() - prefix1.length();
}
};
class InternalResolver
{
protected Map publicMap = new HashMap();
protected Map systemMap = new HashMap();
protected Map uriMap = new HashMap();
// These are sorted by longest "key" first.
protected List rewriteSystemList = new LinkedList();
protected List rewriteUriList = new LinkedList();
protected List suffixSystemList = new LinkedList();
protected List suffixUriList = new LinkedList();
protected List delegatePublicList = new LinkedList();
protected List delegateSystemList = new LinkedList();
protected List delegateUriList = new LinkedList();
InternalResolver()
{
for (Iterator i = catalogElements.iterator(); i.hasNext();)
{
ICatalogElement catalogElement = (ICatalogElement) i.next();
if (catalogElement.getType() == ICatalogElement.TYPE_ENTRY)
{
ICatalogEntry entry = (ICatalogEntry) catalogElement;
Map map = getEntryMap(entry.getEntryType());
map.put(entry.getKey(), entry);
}
else if (catalogElement.getType() == ICatalogElement.TYPE_REWRITE)
{
IRewriteEntry entry = (IRewriteEntry) catalogElement;
if (entry.getEntryType() == IRewriteEntry.REWRITE_TYPE_SYSTEM)
{
rewriteSystemList.add(entry);
}
else
{
rewriteUriList.add(entry);
}
}
else if (catalogElement.getType() == ICatalogElement.TYPE_SUFFIX)
{
ISuffixEntry entry = (ISuffixEntry) catalogElement;
if (entry.getEntryType() == ISuffixEntry.SUFFIX_TYPE_SYSTEM)
{
suffixSystemList.add(entry);
}
else
{
suffixUriList.add(entry);
}
}
else if (catalogElement.getType() == ICatalogElement.TYPE_DELEGATE)
{
IDelegateCatalog delegate = (IDelegateCatalog) catalogElement;
if (delegate.getEntryType() == IDelegateCatalog.DELEGATE_TYPE_PUBLIC)
{
delegatePublicList.add(delegate);
}
else if (delegate.getEntryType() == IDelegateCatalog.DELEGATE_TYPE_SYSTEM)
{
delegateSystemList.add(delegate);
}
else
{
delegateUriList.add(delegate);
}
}
}
Collections.sort(rewriteSystemList, LONGEST_REWRITE_FIRST);
Collections.sort(rewriteUriList, LONGEST_REWRITE_FIRST);
Collections.sort(suffixSystemList, LONGEST_SUFFIX_FIRST);
Collections.sort(suffixUriList, LONGEST_SUFFIX_FIRST);
Collections.sort(delegatePublicList, LONGEST_DELEGATE_PREFIX_FIRST);
Collections.sort(delegateSystemList, LONGEST_DELEGATE_PREFIX_FIRST);
Collections.sort(delegateUriList, LONGEST_DELEGATE_PREFIX_FIRST);
}
private Map getEntryMap(int entryType)
{
Map map = systemMap;
switch (entryType)
{
case ICatalogEntry.ENTRY_TYPE_PUBLIC:
map = publicMap;
break;
case ICatalogEntry.ENTRY_TYPE_URI:
map = uriMap;
break;
default:
break;
}
return map;
}
protected String getMappedURI(Map map, String key)
{
CatalogEntry entry = (CatalogEntry) map.get(key);
if(entry == null) return null;
String uri = entry.getURI();
try
{
// TODO CS : do we really want to resolve these here?
// I'm guessing we should return the 'platform:' form of the URI
// to the caller.
if (uri.startsWith("platform:")) //$NON-NLS-1$
{
URL entryURL = new URL(entry.getAbsolutePath(uri));
uri = Platform.resolve(entryURL).toString();
// we need to ensure URI's are of form "file:///D:/XXX" and NOT
// "file:D:/XXX". Otherwise the EMF URI class gets confused
// (see bug 103607)
String FILE_SCHEME = "file:"; //$NON-NLS-1$
if (uri.startsWith(FILE_SCHEME) && !uri.startsWith(FILE_SCHEME + "/")) //$NON-NLS-1$
{
uri = FILE_SCHEME + "///" + uri.substring(FILE_SCHEME.length()); //$NON-NLS-1$
}
}
return uri;
} catch (IOException e)
{
return null;
}
}
public String resolvePublic(String publicId, String systemId)
throws MalformedURLException, IOException
{
String result = getMappedURI(publicMap, publicId);
if (result == null)
{
result = getMappedURI(systemMap, systemId);
}
// our clients used to pass namespace in place of public id, so we need to check uri map for those
if (result == null)
{
result = getMappedURI(uriMap, publicId);
}
if (result == null)
{
result = resolveDelegateCatalogs(delegatePublicList, publicId, systemId);
}
if (result == null)
{
result = resolveSubordinateCatalogs(
ICatalogEntry.ENTRY_TYPE_PUBLIC, publicId, systemId);
}
return result;
}
public String resolveSystem(String systemId)
throws MalformedURLException, IOException
{
String result = getMappedURI(systemMap, systemId);
if (result == null)
{
result = resolveRewrite(rewriteSystemList, systemId);
}
if (result == null)
{
result = resolveSuffix(suffixSystemList, systemId);
}
if (result == null)
{
result = resolveDelegateCatalogs(delegateSystemList, systemId, systemId); // systemId is the key for "startString"
}
if (result == null)
{
result = resolveSubordinateCatalogs(
ICatalogEntry.ENTRY_TYPE_SYSTEM, null, systemId);
}
return result;
}
private String resolveRewrite(List rewriteList, String searchString)
{
for (Iterator it = rewriteList.iterator(); it.hasNext();)
{
IRewriteEntry entry = (IRewriteEntry) it.next();
String startString = entry.getStartString();
if (searchString.startsWith(startString))
{
return entry.getRewritePrefix() + searchString.substring(startString.length());
}
}
return null;
}
private String resolveSuffix(List suffixList, String searchString) {
for (Iterator it = suffixList.iterator(); it.hasNext();) {
ISuffixEntry entry = (ISuffixEntry) it.next();
if (searchString.endsWith(entry.getSuffix())) {
return entry.getURI();
}
}
return null;
}
protected String resolveDelegateCatalogs(List delegateCatalogs, String key,
String systemId) throws MalformedURLException, IOException
{
String result = null;
for (Iterator iterator = delegateCatalogs.iterator(); iterator
.hasNext();) {
IDelegateCatalog delegate = (IDelegateCatalog) iterator.next();
if (key.startsWith(delegate.getStartString())) {
ICatalog catalog = delegate.getReferencedCatalog();
if (catalog != null)
{
switch (delegate.getEntryType())
{
case IDelegateCatalog.DELEGATE_TYPE_PUBLIC:
result = catalog.resolvePublic(key, systemId);
break;
case IDelegateCatalog.DELEGATE_TYPE_SYSTEM:
result = catalog.resolveSystem(systemId);
break;
case IDelegateCatalog.DELEGATE_TYPE_URI:
result = catalog.resolveURI(systemId);
break;
default:
break;
}
if (result != null)
{
return result;
}
}
}
}
return null;
}
public String resolveURI(String uri) throws MalformedURLException,
IOException
{
String result = getMappedURI(uriMap, uri);
if (result == null)
{
result = resolveRewrite(rewriteUriList, uri);
}
if (result == null)
{
result = resolveSuffix(suffixUriList, uri);
}
if (result == null)
{
result = resolveDelegateCatalogs(delegateUriList, uri, uri); // uri is treated as the systemId
}
if (result == null)
{
result = resolveSubordinateCatalogs(
ICatalogEntry.ENTRY_TYPE_URI, null, uri);
}
return result;
}
}
class SystemCatalogLS extends CatalogLS
{
public void load()
{
new CatalogContributorRegistryReader(Catalog.this).readRegistry();
/*
* Here we save the file in order to 'reflect' the catalog that
* we've created from plugin extensions to disk.
* The 'system' catalog is only ever written to disk and never read from disk.
*/
save();
}
}
class UserCatalogLS extends CatalogLS
{
public void load()
{
InputStream inputStream = null;
try
{
if (location != null && location.length() > 0)
{
URL url = new URL(location);
inputStream = url.openStream();
boolean oldNotificationEnabled = isNotificationEnabled();
setNotificationEnabled(false);
clear();
try
{
CatalogReader.read(Catalog.this, inputStream);
} finally
{
setNotificationEnabled(oldNotificationEnabled);
}
}
else
{
clear();
}
notifyChanged();
} catch (Exception e)
{
// This is OK since the catalog may not exist before we create it
} finally
{
if (inputStream != null)
{
try
{
inputStream.close();
} catch (Exception e)
{
}
}
}
}
}
public static final String DEFAULT_CATALOG_FILE = "default_catalog.xml"; //$NON-NLS-1$
public static final String SYSTEM_CATALOG_FILE = "system_catalog.xml"; //$NON-NLS-1$
public static final String USER_CATALOG_FILE = "user_catalog.xml"; //$NON-NLS-1$
protected String base;
protected List catalogElements = new ArrayList();
protected CatalogLS catalogLS;
protected String id;
protected InternalResolver internalResolver;
protected boolean isNotificationEnabled;
protected List listenerList = new ArrayList();
protected String location;
protected CatalogSet resourceSet;
public Catalog(CatalogSet catalogResourceSet, String id, String location)
{
this.resourceSet = catalogResourceSet;
this.id = id;
this.location = location;
if (XMLCorePlugin.DEFAULT_CATALOG_ID.equals(id))
{
catalogLS = new DefaultCatalogLS();
} else if (XMLCorePlugin.SYSTEM_CATALOG_ID.equals(id))
{
catalogLS = new SystemCatalogLS();
} else
{
catalogLS = new UserCatalogLS();
}
}
public void addCatalogElement(ICatalogElement element)
{
catalogElements.add(element);
element.setOwnerCatalog(this);
internalResolver = null;
notifyAddElement(element);
}
public void addEntriesFromCatalog(ICatalog catalog)
{
try
{
setNotificationEnabled(false);
if (catalog != null)
{
ICatalogElement[] entries = ((Catalog)catalog).getCatalogElements();
for (int i = 0; i < entries.length; i++)
{
CatalogElement clone = (CatalogElement)((CatalogElement)entries[i]).clone();
addCatalogElement(clone);
}
} else
{
Logger.log(Logger.ERROR, "argument was null in Catalog.addEntriesFromCatalog"); //$NON-NLS-1$
}
} finally
{
setNotificationEnabled(true);
}
internalResolver = null;
notifyChanged();
}
public void addListener(ICatalogListener listener)
{
listenerList.add(listener);
}
public void clear()
{
catalogElements.clear();
internalResolver = null;
notifyChanged();
}
public ICatalogElement createCatalogElement(int type)
{
switch (type)
{
case ICatalogElement.TYPE_ENTRY:
return new CatalogEntry(); // TODO: Should be kind of deprecated
case ICatalogElement.TYPE_NEXT_CATALOG:
return new NextCatalog();
case ICatalogEntry.ENTRY_TYPE_PUBLIC:
case ICatalogEntry.ENTRY_TYPE_SYSTEM:
case ICatalogEntry.ENTRY_TYPE_URI:
return new CatalogEntry(type);
case ICatalogElement.TYPE_REWRITE:
case IRewriteEntry.REWRITE_TYPE_SYSTEM:
case IRewriteEntry.REWRITE_TYPE_URI:
return new RewriteEntry(type);
case ICatalogElement.TYPE_SUFFIX:
case ISuffixEntry.SUFFIX_TYPE_SYSTEM:
case ISuffixEntry.SUFFIX_TYPE_URI:
return new SuffixEntry(type);
case ICatalogElement.TYPE_DELEGATE:
case IDelegateCatalog.DELEGATE_TYPE_PUBLIC:
case IDelegateCatalog.DELEGATE_TYPE_SYSTEM:
case IDelegateCatalog.DELEGATE_TYPE_URI:
return new DelegateCatalog(type);
default:
throw new IllegalArgumentException("Unknown element type " + type);//$NON-NLS-1 // Makes no sense at all!
}
}
public String getBase()
{
return base;
}
private List getCatalogElements(int type)
{
List result = new ArrayList();
ICatalogElement[] elements = (ICatalogElement[]) catalogElements
.toArray(new ICatalogElement[catalogElements.size()]);
for (int i = 0; i < elements.length; i++)
{
ICatalogElement element = elements[i];
if (element.getType() == type)
{
result.add(element);
}
}
return result;
}
public ICatalogEntry[] getCatalogEntries()
{
List result = getCatalogElements(ICatalogElement.TYPE_ENTRY);
return (ICatalogEntry[]) result
.toArray(new ICatalogEntry[result.size()]);
}
public IDelegateCatalog[] getDelegateCatalogs()
{
List result = getCatalogElements(ICatalogElement.TYPE_DELEGATE);
return (IDelegateCatalog[]) result
.toArray(new IDelegateCatalog[result.size()]);
}
public IRewriteEntry[] getRewriteEntries()
{
List result = getCatalogElements(ICatalogElement.TYPE_REWRITE);
return (IRewriteEntry[]) result
.toArray(new IRewriteEntry[result.size()]);
}
public ISuffixEntry[] getSuffixEntries()
{
List result = getCatalogElements(ICatalogElement.TYPE_SUFFIX);
return (ISuffixEntry[]) result
.toArray(new ISuffixEntry[result.size()]);
}
protected CatalogSet getCatalogSet()
{
return resourceSet;
}
public String getId()
{
return id;
}
public String getLocation()
{
return location;
}
public INextCatalog[] getNextCatalogs()
{
List result = getCatalogElements(ICatalogElement.TYPE_NEXT_CATALOG);
return (INextCatalog[]) result.toArray(new INextCatalog[result.size()]);
}
protected InternalResolver getOrCreateInternalResolver()
{
if (internalResolver == null)
{
internalResolver = new InternalResolver();
}
return internalResolver;
}
protected boolean isNotificationEnabled()
{
return isNotificationEnabled;
}
public void load() throws IOException
{
catalogLS.load();
}
protected void notifyAddElement(ICatalogElement entry)
{
if (isNotificationEnabled)
{
ICatalogEvent event = new CatalogEvent(this, entry,
ICatalogEvent.ELEMENT_ADDED);
notifyListeners(event);
}
}
protected void notifyChanged()
{
ICatalogEvent event = new CatalogEvent(this, null,
ICatalogEvent.CHANGED);
notifyListeners(event);
}
protected void notifyListeners(ICatalogEvent event)
{
List list = new ArrayList();
list.addAll(listenerList);
for (Iterator i = list.iterator(); i.hasNext();)
{
ICatalogListener listener = (ICatalogListener) i.next();
listener.catalogChanged(event);
}
}
protected void notifyRemoveElement(ICatalogElement element)
{
if (isNotificationEnabled)
{
ICatalogEvent event = new CatalogEvent(this, element,
ICatalogEvent.ELEMENT_REMOVED);
notifyListeners(event);
}
}
public void removeCatalogElement(ICatalogElement element)
{
catalogElements.remove(element);
internalResolver = null;
notifyRemoveElement(element);
}
public void removeListener(ICatalogListener listener)
{
listenerList.remove(listener);
}
public String resolvePublic(String publicId, String systemId)
throws MalformedURLException, IOException
{
return getOrCreateInternalResolver().resolvePublic(publicId, systemId);
}
protected String resolveSubordinateCatalogs(int entryType, String publicId,
String systemId) throws MalformedURLException, IOException
{
String result = null;
INextCatalog[] nextCatalogs = getNextCatalogs();
for (int i = 0; i < nextCatalogs.length; i++)
{
INextCatalog nextCatalog = nextCatalogs[i];
ICatalog catalog = nextCatalog.getReferencedCatalog();
if (catalog != null)
{
switch (entryType)
{
case ICatalogEntry.ENTRY_TYPE_PUBLIC:
result = catalog.resolvePublic(publicId, systemId);
break;
case ICatalogEntry.ENTRY_TYPE_SYSTEM:
result = catalog.resolveSystem(systemId);
break;
case ICatalogEntry.ENTRY_TYPE_URI:
result = catalog.resolveURI(systemId);
break;
default:
break;
}
if (result != null)
{
return result;
}
}
}
return null;
}
public String resolveSystem(String systemId) throws MalformedURLException,
IOException
{
return getOrCreateInternalResolver().resolveSystem(systemId);
}
public String resolveURI(String uri) throws MalformedURLException,
IOException
{
return getOrCreateInternalResolver().resolveURI(uri);
}
public void save() throws IOException
{
catalogLS.save();
}
public void setBase(String base)
{
this.base = base;
}
public void setId(String id)
{
this.id = id;
}
public void setLocation(String location)
{
this.location = location;
}
protected void setNotificationEnabled(boolean b)
{
isNotificationEnabled = b;
}
public ICatalogElement[] getCatalogElements()
{
return (ICatalogElement[]) catalogElements.toArray(new ICatalogElement[catalogElements.size()]);
}
}