blob: 97d63d11713cdf8c02c10c454cad74f6840d2a42 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jem.internal.proxy.core;
import java.lang.reflect.Array;
import java.util.*;
import java.util.regex.Pattern;
/**
* Mapping of container paths (Regular expressions) to configuration elements.
* <p>
* It can be instantiated by clients that need to built up a subset of the mapping from the
* normal mapping done by {@link ProxyPlugin#getPluginExtensions(String)}.
* <p>
* To build one up separately clients would:
* <pre><code>
* ContainerPathContributionMapping mapping = new ContainerPathContributionMapping(contributionType);
* mapping.addContribution(containerid, pattern, contribution);
* ...
* mapping.finalizeMapping();
* </code></pre>
*
* @since 1.2.0
*/
public class ContainerPathContributionMapping {
/**
* Used in {@link ContainerPathContributionMapping#containerIdToContributions} as the value of the map.
* This contains a Pattern for a container path to match against to see if the contribution
* should be used.
* <p>
* Normally it would be better to use {@link ContainerPathContributionMapping#getContributors(String, String[])}
* to get all of the contributors for all of the paths of the same container id.
*
* @since 1.2.0
*/
public static class ContainerContributionEntry {
private final Object contribution;
private final Pattern containerPathPattern;
public ContainerContributionEntry(Object contribution, Pattern containerPathPattern) {
this.contribution = contribution;
this.containerPathPattern = containerPathPattern;
}
/**
* @return Returns the contribution.
*
* @since 1.2.0
*/
public final Object getContribution() {
return contribution;
}
/**
* @return Returns the container pattern for matching.
*
* @since 1.2.0
*/
public final Pattern getContainerPathPattern() {
return containerPathPattern;
}
}
/**
* Map of container ids (String, first segment of container path) to {@link ContainerContributionEntry[]}.
* <p>
* Normally it would be better to use {@link #getContributors(String, String[])}
* to get all of the contributors for all of the paths of the same container id.
*
* @since 1.2.0
*/
public Map containerIdToContributions = Collections.EMPTY_MAP;
// TODO When 1.5, this can go away.
private final Class contributionType;
/**
* Create with contribution type (i.e. the array type returned from {@link #getContributors(String, String[])}.
* <p>
* TODO This will go away with 1.5 because this will be templated.
*
* @param type
*
*
* @since 1.2.0
*/
public ContainerPathContributionMapping(Class type) {
contributionType = type;
}
/**
* Get the unique array of configuration elements (no duplicates) for the given container id, and
* the set of container paths for that container id. For example, "SWT_CONTAINER" as containerID and
* {"/SWT_CONTAINER/", "/SWT_CONTAINER/PDE/JFACE"} for container paths. This will then return configuration elements
* that match these two paths in the container attribute of the configuration element. The container attribute
* is a regular expression. For example "SWT_CONTAINER" will match both "/SWT_CONTAINER/" and "/SWT_CONTAINER/PDE/JFACE"
* while "SWT_CONTAINER/.* /JFACE" will match "/SWT_CONTAINER/PDE/JFACE". (Note it is actually no space between the "*" and "/" but java comment syntax won't allow it.)
* @param containerId id of all the containers in the list of paths (the first segment of all of the paths).
* @param containerPaths array of all of the paths to look for contributions for.
* @return array of configuration elements for the given list of paths. They will be in order declared within a plugin within plugin order.
*
* @since 1.2.0
*/
public Object[] getContributors(String containerId, String[] containerPaths) {
ContainerContributionEntry[] bundleContributions = (ContainerContributionEntry[]) containerIdToContributions.get(containerId);
if (bundleContributions == null)
return (Object[]) Array.newInstance(contributionType, 0);
List contributions = new ArrayList();
// Patterns that have been tested. Key is a pattern, value is Boolean. true if this pattern matched any of the container paths.
// This way a pattern will only be tested once for the list of paths. If the pattern is found the list again we will know if it
// should be selected or not.
// The bundleContributions are in order declared within each plugin within plugin order.
Map testedPatterns = new HashMap();
for (int i = 0; i < bundleContributions.length; i++) {
Boolean tested = (Boolean) testedPatterns.get(bundleContributions[i].getContainerPathPattern());
if (tested == null) {
// Need to test it.
// Run through container paths and see if any match.
tested = Boolean.FALSE;
Pattern pattern = bundleContributions[i].getContainerPathPattern();
for (int j = 0; j < containerPaths.length; j++) {
if (pattern.matcher(containerPaths[j]).matches()) {
tested = Boolean.TRUE;
break;
}
}
testedPatterns.put(pattern, tested);
}
if (tested.booleanValue())
contributions.add(bundleContributions[i].getContribution());
}
return contributions.toArray((Object[]) Array.newInstance(contributionType, contributions.size()));
}
/**
* Add contribution to mapping. This is not normally needed by clients unless the client needs to
* build up a different container path mapping than the one normally created by {@link ProxyPlugin#processContributionExtensionPoint(String)}.
* <p>
* After all contributions have been added {@link #finalizeMapping()} must be called. If this
* is not called then {@link #getContributors(String, String[])} will fail with exceptions.
* @param containerId
* @param pattern
* @param contribution
*
* @since 1.2.0
*/
public void addContribution(String containerId, Pattern pattern, Object contribution) {
if (containerIdToContributions == Collections.EMPTY_MAP)
containerIdToContributions = new HashMap(); // This is first call to add something.
// We will build as list, but then change to array when done.
Object contributions = containerIdToContributions.get(containerId);
if (contributions == null) {
contributions = new ArrayList(1);
containerIdToContributions.put(containerId, contributions);
} else if (!(contributions instanceof List)) {
// It must be an array, so convert back to list so that we can add to it.
List oldContributions = Arrays.asList((Object[]) contribution);
contributions = new ArrayList(oldContributions.size());
((List) contributions).addAll(oldContributions);
containerIdToContributions.put(containerId, contributions);
}
((List) contributions).add(new ContainerContributionEntry(contribution, pattern));
}
/**
* Finalize the mapping. This is called when clients are done with all {@link #addContribution(String)}.
* It takes the mapping from an internal format that allowed for quicker building into the final format.
*
*
* @since 1.2.0
*/
public void finalizeMapping() {
for (Iterator iter = containerIdToContributions.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
if (entry.getValue() instanceof List)
entry.setValue(((List) entry.getValue()).toArray(new ContainerContributionEntry[((List) entry.getValue()).size()]));
}
}
}