/***************************************************************************** * Copyright (c) 2008 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Mathias Stuempert - initial API and implementation *****************************************************************************/ package eu.geclipse.core.internal.model; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import eu.geclipse.core.Extensions; import eu.geclipse.core.ICoreProblems; import eu.geclipse.core.internal.Activator; import eu.geclipse.core.model.ICreatorSourceMatcher; import eu.geclipse.core.model.IGridElement; import eu.geclipse.core.model.IGridElementCreator; import eu.geclipse.core.reporting.ProblemException; /** * An internally used reference to an extension of the * <code>eu.geclipse.core.gridElementCreator</code> extension point. */ public class ElementCreatorReference { /** * Matches a source object with the source information of the corresponding * element creator extension. The matched object may be a {@link Class} in * which case the matching does only take into account the source type and * not the pattern or the specified {@link ICreatorSourceMatcher}. */ private static class SourceMatcher { /** * The default priority taken if no priority is specified in the extension. */ private static final int DEFAULT_PRIORITY = 50; /** * The type of the supported source. */ private Class< ? > sourceClass; /** * An optional pattern matched against the toString() result of the matched * object. */ private Pattern sourcePattern; /** * The priority of the creator concerning this source matcher. */ private int sourcePriority; /** * Specifies if this is the default source of the corresponding creator. */ private boolean sourceDefault; /** * An optional source matcher for more sophisticated source matching * patterns. */ private ICreatorSourceMatcher sourceMatcher; /** * Standard constructor. * * @param element The {@link IConfigurationElement} this * {@link SourceMatcher} should refer to. * @throws ProblemException If the matcher could not be created. Possible * reasons include class loading problems for the source class and source * matcher or invalid regular expressions for the source pattern. */ SourceMatcher( final IConfigurationElement element ) throws ProblemException { // Get the base classes for the class loading String srcclsatt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_SOURCE_CLASS_ATTRIBUTE ); String srccntr = element.getContributor().getName(); Bundle bundle = Platform.getBundle( srccntr ); // Try to load the source class from its home bundle if ( bundle != null ) { try { this.sourceClass = bundle.loadClass( srcclsatt ); } catch( ClassNotFoundException cnfExc ) { String idatt = ( ( IConfigurationElement ) element.getParent() ).getAttribute( Extensions.GRID_ELEMENT_CREATOR_ID_ATTRIBUTE ); throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.source_class_loading_failed"), srcclsatt, idatt ), //$NON-NLS-1$ cnfExc, Activator.PLUGIN_ID ); } } else { throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.source_bundle_loading_failed"), srccntr, srcclsatt ), //$NON-NLS-1$ Activator.PLUGIN_ID ); } // Parse and compile the source pattern String srcptrnatt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_SOURCE_PATTERN_ATTRIBUTE ); if ( srcptrnatt != null ) { try { this.sourcePattern = Pattern.compile( srcptrnatt ); } catch ( PatternSyntaxException psExc ) { String idatt = ( ( IConfigurationElement ) element.getParent() ).getAttribute( Extensions.GRID_ELEMENT_CREATOR_ID_ATTRIBUTE ); throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.invalid_source_pattern"), srcptrnatt, idatt ), //$NON-NLS-1$ Activator.PLUGIN_ID ); } } String srcprrtyatt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_SOURCE_PRIORITY_ATTRIBUTE ); if ( srcprrtyatt != null ) { try { this.sourcePriority = Integer.parseInt( srcprrtyatt ); if ( this.sourcePriority < 1 ) { this.sourcePriority = 1; } else if ( this.sourcePriority > 99 ) { this.sourcePriority = 99; } } catch ( NumberFormatException nfExc ) { throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, Messages.getString("ElementCreatorReference.invalid_priority"), //$NON-NLS-1$ Activator.PLUGIN_ID ); } } else { this.sourcePriority = DEFAULT_PRIORITY; } // Parse the default attribute String srcdfltatt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_SOURCE_DEFAULT_ATTRIBUTE ); if ( srcdfltatt != null ) { this.sourceDefault = Boolean.parseBoolean( srcdfltatt ); } else { this.sourceDefault = false; } // Parse and eventually create the source matcher. String srcmtchratt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_SOURCE_MATCHER_ATTRIBUTE ); if ( srcmtchratt != null ) { try { this.sourceMatcher = ( ICreatorSourceMatcher ) element.createExecutableExtension( Extensions.GRID_ELEMENT_CREATOR_SOURCE_MATCHER_ATTRIBUTE ); } catch( CoreException cExc ) { String idatt = ( ( IConfigurationElement ) element.getParent() ).getAttribute( Extensions.GRID_ELEMENT_CREATOR_ID_ATTRIBUTE ); throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.source_matcher_loading_failed"), srcmtchratt, idatt ), //$NON-NLS-1$ cExc, Activator.PLUGIN_ID ); } } } /** * Get the priority assigned to this source matcher. * * @return The matcher's priority. */ public int getPriority() { return this.sourcePriority; } /** * Determine if this is the default source for the corresponding creator. * * @return <code>true</code> if this is the default source. */ public boolean isDefault() { return this.sourceDefault; } /** * Matches the specified object with the requirements of this source * matcher. If the specified source is a {@link Class} the matching * algorithm only takes into account the source's type. If the object is not * a {@link Class} the object's type and it's toString() result are matched. * If both matches are successful an optional {@link ICreatorSourceMatcher} * is applied as last step. * * @param source The source object to be matched. * @return <code>true</code> if the specified object matches all * requirements of this source matcher. */ public boolean matches( final Object source ) { boolean result = false; if ( source instanceof Class< ? > ) { result = this.sourceClass.isAssignableFrom( ( Class< ? > ) source ); } else { result = this.sourceClass.isAssignableFrom( source.getClass() ); if ( result && ( this.sourcePattern != null ) ) { result = this.sourcePattern.matcher( source.toString() ).matches(); } if ( result && ( this.sourceMatcher != null ) ) { result = this.sourceMatcher.canCreate( source ); } } return result; } } /** * Matches a target element with the target information of the corresponding * element creator extension. */ private static class TargetMatcher { /** * The type of the supported target. */ private Class< ? > targetClass; /** * The target's name. */ private String targetName; /** * Standard constructor. * * @param element The {@link IConfigurationElement} this * {@link TargetMatcher} should refer to. * @throws ProblemException If the matcher could not be created. Possible * reasons include class loading problems for the target class. */ TargetMatcher( final IConfigurationElement element ) throws ProblemException { // Get the base classes for the class loading String trgtclsatt = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_TARGET_CLASS_ATTRIBUTE ); String trgtcntr = element.getContributor().getName(); Bundle bundle = Platform.getBundle( trgtcntr ); // Try to load the target class from its home bundle if ( bundle != null ) { try { this.targetClass = bundle.loadClass( trgtclsatt ); } catch( ClassNotFoundException cnfExc ) { String idatt = ( ( IConfigurationElement ) element.getParent() ).getAttribute( Extensions.GRID_ELEMENT_CREATOR_ID_ATTRIBUTE ); throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.target_class_loading_failed"), trgtclsatt, idatt ), //$NON-NLS-1$ cnfExc, Activator.PLUGIN_ID ); } } else { throw new ProblemException( ICoreProblems.MODEL_ELEMENT_CREATE_FAILED, String.format( Messages.getString("ElementCreatorReference.target_bundle_loading_failed"), trgtcntr, trgtclsatt ), //$NON-NLS-1$ Activator.PLUGIN_ID ); } // Load the target name this.targetName = element.getAttribute( Extensions.GRID_ELEMENT_CREATOR_TARGET_NAME_ATTRIBUTE ); } /** * Get the name of the target. * * @return The target's name. */ public String getName() { return this.targetName; } /** * Matches the specified class with the requirements of this target * matcher. * * @param target The target class to be matched. * @return <code>true</code> if the specified class matches the target type. */ public boolean matches( final Class< ? extends IGridElement > target ) { return target.isAssignableFrom( this.targetClass ); } } /** * The {@link IConfigurationElement} corresponding to this reference. */ private IConfigurationElement configurationElement; /** * The instantiated element creator specified in the extension. */ private IGridElementCreator elementCreator; /** * The source matchers. */ private List< SourceMatcher > sourceMatchers; /** * The target matcher. */ private TargetMatcher targetMatcher; /** * Create a new creator reference from the specified * {@link IConfigurationElement}. * * @param configurationElement The {@link IConfigurationElement} corresponding * to this reference. */ ElementCreatorReference( final IConfigurationElement configurationElement ) { this.configurationElement = configurationElement; } /** * Test if the specified source object is supported by the corresponding * element creator. * * @param source The source to be checked. * @return A number between 1 and 99 inclusive if the source is supported by * the corresponding element creator. The number corresponds to the creators * priority. If the source is not supported -1 will be returned. If source is * specified as <code>null</code> this method returns a value of 0. * @throws ProblemException If a problem occurs. * @see SourceMatcher#matches(Object) */ public int checkSource( final Object source ) throws ProblemException { int result = 0; if ( source != null ) { result = -1; if ( this.sourceMatchers == null ) { initSourceMatchers(); } for ( SourceMatcher matcher : this.sourceMatchers ) { if ( matcher.matches( source ) ) { int priority = matcher.getPriority(); if ( priority > result ) { result = priority; } } } } return result; } /** * Test if the specified target type is supported by the corresponding * element creator. * * @param target The target to be checked. * @return <code>true</code> if the target is supported by the corresponding * element creator. * @throws ProblemException If a problem occurs. * @see TargetMatcher#matches(Class) */ public boolean checkTarget( final Class< ? extends IGridElement > target ) throws ProblemException { boolean result = target == null; if ( target != null ) { if ( this.targetMatcher == null ) { initTargetMatcher(); } if ( this.targetMatcher.matches( target ) ) { result = true; } } return result; } /** * Get the {@link IConfigurationElement} that is associated to this reference. * * @return This reference's associated configuration element. */ public IConfigurationElement getConfigurationElement() { return this.configurationElement; } /** * Get the {@link IGridElementCreator} from the corresponding extension. If * the element creator was not yet created an attempt is made to create it * now. If this attempt fails an error is logged and the methods returns * <code>null</code>. * * @return The associated {@link IGridElementCreator} or <code>null</code> if * the creation of the creator failed. */ public IGridElementCreator getElementCreator() { if ( this.elementCreator == null ) { try { this.elementCreator = ( IGridElementCreator ) this.configurationElement.createExecutableExtension( Extensions.GRID_ELEMENT_CREATOR_EXECUTABLE ); } catch( CoreException cExc ) { Activator.logException( cExc ); } } return this.elementCreator; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "ElementCreatorReference@" //$NON-NLS-1$ + this.configurationElement.getAttribute( Extensions.GRID_ELEMENT_CREATOR_ID_ATTRIBUTE ); } /** * Initialize the reference's source matchers. * * @throws ProblemException If the initialization failed. */ private void initSourceMatchers() throws ProblemException { this.sourceMatchers = new ArrayList< SourceMatcher >(); IConfigurationElement[] children = this.configurationElement.getChildren( Extensions.GRID_ELEMENT_CREATOR_SOURCE_ELEMENT ); for ( IConfigurationElement child : children ) { this.sourceMatchers.add( new SourceMatcher( child ) ); } } /** * Initialize the reference's target matcher. * * @throws ProblemException If the initialization failed. */ private void initTargetMatcher() throws ProblemException { IConfigurationElement[] children = this.configurationElement.getChildren( Extensions.GRID_ELEMENT_CREATOR_TARGET_ELEMENT ); this.targetMatcher = new TargetMatcher( children[ 0 ] ); } }