Thursday, January 17, 2008

Load XSDs Without Dependencies

Recently my colleague, Jeff Ramsdale, and I were faced with an Eclipse XSD problem: how can we load the contents of an XSD Resource without loading its transitive dependencies?
In our particular situation, we have the following requirements:
  • There are some logical groupings of xsd files.
  • In each grouping there are upwards of 150 schemas, some of which are very large.
  • Many of these schemas refer to one another or refer to schemas that refer to other schemas, etc.
  • We have a TreeViewer that displays these groups and we need to display the schemas in each group and the contents of each schema.
  • We have a custom ItemProviderAdapter for each schema group, and each group's children is a simple object that displays the schema name.
  • For each schema, the getChildren method of the ItemProviderAdapter uses a ResourceSet to demand-load the contents of the XSDResourceImpl to get its contents.
We found that the top level groups were displayed very quickly, but when we tried to open each schema (i.e., load it from the ResourceSet), it could take any number of seconds (10-20 in some cases) to display the schema contents.
Some schemas were causing many transitive dependencies to be loaded into the ResourceSet, even though the contents of those schemas were not displayed in the UI.
The solution, which we implemented thanks to a suggestion by Ed Merks, is to not use the ResourceSet for loading the XSDResourceImpl. We found that we could get the schema contents in much less time and without loading dependent resources just by instantiating the XSDResourceImpl directly with its corresponding URI.
Below I will show you how to set up a test case to demonstrate this solution.

Create a New Eclipse Project


Goto File -> New -> Other -> Plug-in Development -> Plug-in Project and create a project called transitive.dependencies.example.
We will need to declare the following Plug-in dependencies in the MANIFEST.MF
  • org.junit4
  • org.eclipse.emf
  • org.eclipse.emf.ecore
  • org.eclipse.xsd

Plug-in dependencies

Create XSDs With Transitive Dependencies


For this example I created 3 schemas (a.xsd, b.xsd and c.xsd) with transitive dependencies such that a.xsd refers to b.xsd, which refers to c.xsd, which refers to xml.xsd.
The three schemas are below and can be dropped into the folder /transitive.dependencies.example/xsd in our workspace.

a.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://transitive.dependencies.example/a"
xmlns:b="http://transitive.dependencies.example/b"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://transitive.dependencies.example/a">
<xsd:annotation>
<xsd:documentation>schema a ($Date: 2008/01/16 00:14:59 $) </xsd:documentation>
</xsd:annotation>
<xsd:import namespace="http://transitive.dependencies.example/b"
schemaLocation="xsd/b.xsd"/>
<xsd:attributeGroup name="a.attributes">
<xsd:attribute ref="b:b.type"/>
</xsd:attributeGroup>
</xsd:schema>


b.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://transitive.dependencies.example/b"
xmlns:c="http://transitive.dependencies.example/c"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://transitive.dependencies.example/b">
<xsd:annotation>
<xsd:documentation>schema b ($Date: 2008/01/16 00:14:59 $) </xsd:documentation>
</xsd:annotation>
<xsd:import namespace="http://transitive.dependencies.example/c"
schemaLocation="xsd/c.xsd"/>
<xsd:attribute name="b.type" type="xsd:anyURI"/>
<xsd:attributeGroup name="b.attributes">
<xsd:attribute ref="c:c.type"/>
</xsd:attributeGroup>
</xsd:schema>


c.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://transitive.dependencies.example/c"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://transitive.dependencies.example/c">
<xsd:annotation>
<xsd:documentation>schema c ($Date: 2008/01/16 00:14:59 $) </xsd:documentation>
</xsd:annotation>
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<xsd:attribute name="c.type" type="xsd:anyURI"/>
<xsd:attributeGroup name="c.attributes">
<xsd:attribute ref="xml:id"/>
<xsd:attribute ref="xml:lang"/>
</xsd:attributeGroup>
</xsd:schema>


Create a New Test Case


For our first test, we want to show that when we demand-load an XSDResourceImpl through a ResourceSet, all of its transitive dependencies are also loaded into the ResourceSet.


package transitive.dependencies.example;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xsd.impl.XSDSchemaImpl;
import org.eclipse.xsd.util.XSDResourceImpl;
import org.junit.Test;

public class LoadXsdTest {

@Test
public void demandLoadFromResourceSet() throws Exception {
final ResourceSet resourceSet = XSDSchemaImpl.createResourceSet();
assertNotNull(resourceSet.getResource(URI.createFileURI("xsd/a.xsd"),
true));
assertEquals(4, resourceSet.getResources().size());
assertNotNull(resourceSet.getResource(URI.createFileURI("xsd/b.xsd"),
false));
assertNotNull(resourceSet.getResource(URI.createFileURI("xsd/c.xsd"),
false));
assertNotNull(resourceSet.getResource(
URI.createURI("http://www.w3.org/2001/xml.xsd"), false));
}
}



For our next test, we want to show that when we load an orphaned XSDResourceImpl and manually put it into and retrieve it from the ResourceSet, that its dependencies are not automatically resolved.


....
@Test
public void createAndAddDetachedResources() throws Exception {
final ResourceSet resourceSet = XSDSchemaImpl.createResourceSet();
final URI uri = URI.createFileURI("xsd/a.xsd");
resourceSet.getResources().add(new XSDResourceImpl(uri));
assertEquals(1, resourceSet.getResources().size());

assertNotNull(resourceSet.getResource(uri, false));
assertEquals(1, resourceSet.getResources().size());
}
....


Interestingly, in the second test case, if we set the demandLoad flag to true when we get the XSDResourceImpl from the ResourceSet, the ResourceSet will load all the dependent resources, even though a.xsd has already been loaded and is present in the ResourceSet.  In that case, the second assertion will fail.

Both of the above test cases should pass as written.  Run them to get a green bar!

Friday, January 11, 2008

Dynamic Labels Redux

This is an update to a previous blog posting about contributing menu items with dynamic labels to popups. The constructor

public CommandContributionItem(IServiceLocator serviceLocator, String id, String commandId, Map parameters, ImageDescriptor icon, ImageDescriptor disabledIcon, ImageDescriptor hoverIcon, String label, String mnemonic, String tooltip, int style)

has been deprecated in Ganymede (Eclipse 3.4) in favor of the less unwieldy

public CommandContributionItem(CommandContributionItemParameter contributionParameters)

CommandContributionItemParameter has both a complete and a minimal constructor (for injecting just the required parameters), and its fields are public and non-final, so they can be set as needed.

The equivalent revised example class looks something like this:


public class MyCompoundContributionItem extends CompoundContributionItem {
private static int counter = 0;

@Override
protected IContributionItem[] getContributionItems() {
final CommandContributionItemParameter contributionParameter =
new CommandContributionItemParameter(
PlatformUI.getWorkbench().getActiveWorkbenchWindow(),
"my.project.myCommandContributionItem", "my.project.myCommand",
SWT.NONE);
contributionParameter.label = "Dynamic Menu Item " + counter++;
return new IContributionItem[] {
new CommandContributionItem(contributionParameter) };
}
}

Monday, January 7, 2008

Eclipse 3.4M4 solaris-gtk-x86 Build Script for Nexenta

The following script is based on another script for compiling Eclipse 3.3 for Nexenta (solaris-gtk-x86). The build process changed slightly from version 3.3, particularly in the native compilation of the eclipse launcher. If you are curious to see what needed to change, you can diff the 3.4M4 and 3.3 scripts. I have not used Eclipse 3.4M4 extensively yet, but from some initial trials, the version I have built with this script seems to be working well. As always, if this post has been of any use, please let me know.
You should be able to drop the script into a directory that contains the eclipse-sourceBuild-srcIncluded-3.4M4.zip, execute it, and it will decompress the archive, modify the build scripts to support nexenta with gtk on x86, and create a distibutable file in the result directory.

The script can now be downloaded from the solipse downloads page.  Be aware of the Nexenta-specific configurations for compiling the SWT libraries.


The Ganymede splash-screen on nexenta solaris-gtk-x86.
The Ganymede splash-screen on nexenta solaris-gtk-x86.

The Ganymede workbench on nexenta solaris-gtk-x86.
The Ganymede workbench on nexenta solaris-gtk-x86.

Eclipse 3.3 solaris-gtk-x86 Build Script for Nexenta

The following script was extracted from another blog posting about compiling Eclipse 3.3 for Nexenta (solaris-gtk-x86). I have separated it out and modified the original post because most of the steps for figuring-out dependencies are valid for building Eclipse 3.4M4 and building Eclipse 3.4M5, but the build itself is slightly different.
I have used this script to build Eclipse 3.3 for myself, have since programmed a bit with my version so I know it has worked for my basic needs and would be curious to know if anyone else has gotten any use out of it.
The script itself makes extensive use of the simple command-line find-and-replace syntax I wrote about here.
You should be able to drop the script into a directory that contains the eclipse-sourceBuild-srcIncluded-3.3.zip, execute it, and it will decompress the archive, modify the build scripts to support nexenta with gtk on x86, and create a distibutable file in the result directory.
The script can now be downloaded from the solipse downloads page.  Be aware of the Nexenta-specific configurations for compiling the SWT libraries.
 


The Europa splash-screen on nexenta solaris-gtk-x86.
The Europa splash-screen on nexenta solaris-gtk-x86.

The Europa workbench on nexenta solaris-gtk-x86.
The Europa workbench on nexenta solaris-gtk-x86.