Eclipse editor plug-in: syntax highlighting - eclipse

I'm working on an editor plugin for a custom language and I've managed to set it up so all the necessary keywords highlight. The problem is the words become highlighted even if they are part of another word.
For example: let's say public is a keyword and I initialize a variable called publicVar so it looks like this public int publicVar. public highlights as expected but the 'public' part of publicVar is also highlighted which is not what I want.
public WFSPartitionScanner()
{
int index = 0;
int numOfRules = 5 + reversedWords.length + commonFunctions.length+directives.length +
BIFs.length + operators.length + strongOperators.length;
IToken string = new Token(WFS_STRING);
IToken comment = new Token(WFS_COMMENT);
IToken reversedWord = new Token(WFS_REVERSED_WORD);
IToken commonFunction = new Token(WFS_COMMON_FUNCTION);
IToken directive = new Token(WFS_DIRECTIVE);
IToken bif = new Token(WFS_BIF);
IToken operator = new Token(WFS_OPERATOR);
IToken strongOperator = new Token(WFS_STRONG_OPERATOR);
IToken numberToken = new Token(WFS_NUMBER);
IPredicateRule[] rules= new IPredicateRule[numOfRules];
rules[index] = new MultiLineRule("\"","\"", string, '\\');
rules[++index] = new MultiLineRule("\'", "\'", string, '\\');
rules[++index] = new SingleLineRule("//","\n", comment);
rules[++index] = new MultiLineRule("/*", "*/", comment);
rules[++index] = new WFSNumberRule(numberToken);
for(int i = 0; i < reversedWords.length; i++)
{
rules[++index] = new WordPatternRule(new WordDetector(reversedWords[i]), reversedWords[i], "",reversedWord);
}
for(int i = 0; i < commonFunctions.length; i++)
{
rules[++index] = new WordPatternRule(new WordDetector(commonFunctions[i]), commonFunctions[i], "",commonFunction);
}
for(int i = 0; i < BIFs.length;i++)
{
rules[++index] = new WordPatternRule(new WordDetector(BIFs[i]), BIFs[i], "",bif);
}
for(int i = 0; i < directives.length;i++)
{
rules[++index] = new WordPatternRule(new WordDetector(directives[i]), directives[i], "",directive);
}
for(int i=0; i < operators.length; i++)
{
rules[++index]= new WordPatternRule(new WordDetector(operators[i]), operators[i], "", operator);
}
for(int i=0; i < strongOperators.length; i++)
{
rules[++index]= new WordPatternRule(new WordDetector(strongOperators[i]), strongOperators[i], "", strongOperator);
}
setPredicateRules(rules);
}
public class WordDetector implements IWordDetector{
private char start;
private char[] part;
public WordDetector(String word)
{
this.start = word.charAt(0);
this.part = new char[word.length() - 1];
for(int i = 1; i < word.length(); i++)
{
part[i-1] = word.charAt(i);
}
}
#Override
public boolean isWordPart(char c) {
for(int i = 0; i < part.length; i++)
{
if(c == part[i])
{
return true;
}
}
return false;
}
#Override
public boolean isWordStart(char c) {
return (c == start);
}
}
I've also tried changing the WordPatternRule from
WordPatternRule(new WordDetector('KEYWORD'), 'KEYWORD', "",reversedWord);
to
WordPatternRule(new WordDetector('KEYWORD'), 'FIRST LETTER OF KEYWORD', 'LAST LETTER OF KEYWORD,reversedWord);
but I got the same results.

Ok I figured out part of the problem. To fix it i made my own WORDRULE
public class WFSWordRule extends WordRule implements IPredicateRule{
private IToken successToken;
public WFSWordRule(IWordDetector detector, String[] keywords, IToken token ) {
super(detector);
this.successToken= token;
for (String word : keywords) {
addWord(word, token);
}
}
#Override
public IToken evaluate(ICharacterScanner scanner, boolean arg1) {
return super.evaluate(scanner);
}
#Override
public IToken getSuccessToken() {
return successToken;
}
}
this doesn't completely solve the problem though. Now if there is a keyword at the end of a word the keyword is still highlighted. Using the same example as before, if i have a keyword 'Public' and i have a variable called 'varPublic' public is highlighted in both cases. But if i have a variable called 'PublicVar' public is not highlighted. any tips?

If you're trying to highlight words, not parts of words, I think you should use WordRule instead of WordPatternRule, as you have. From what I understand, WordPatternRule is used for finding patterns within a word, whereas WordRule is used for finding individual words.
I've used WordRules in an Eclipse plug in which I've been working on, and I don't have the problem of words being highlighted within other words. You can look at its code and use it as an example. Basically, it takes an IWordDetector implementation and you use its addWord method to add all the words you want it to detect.
Unlike CombinedWordRule, which greg-449 mentioned, it's a public part of the JFace framework which you are already using.

Not sure, if this is still useful, but I fixed this issue by overriding the method nextToken in my Scanner:
public IToken nextToken() {
if (this.fContentType == null || this.fRules == null) {
// don't try to resume
this.fTokenOffset = this.fOffset;
this.fColumn = RuleBasedScanner.UNDEFINED;
if (this.fRules != null) {
for (IRule fRule : this.fRules) {
IToken token = fRule.evaluate(this);
try {
if (!token.isUndefined()) {
if (this.fTokenOffset > 0 && (isWordEnd(this.fDocument.getChar(this.fTokenOffset - 1))
|| isSpecialKey(this.fDocument.getChar(this.fTokenOffset)))) {
this.fContentType = null;
return token;
}
}
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
if (this.read() == ICharacterScanner.EOF) {
return Token.EOF;
}
return this.fDefaultReturnToken;
}
}
isWordEnd checks for:
c == (char) 0 || c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')' || c == ',' || c == ';'
isSpecialKey checks for:
c == '%'
For the other Issue, you've already fixed, I reimplemented the endSequenceDetected method in my rule:
#Override
protected boolean endSequenceDetected(ICharacterScanner scanner) {
int c = scanner.read();
scanner.unread();
return isWordEnd((char) c);
}

Related

Auto resize row heights while sorting or filtering

I can not handle auto row heights resizing.
I tried to use AutomaticRowHeightExample, but without success.
Another inspiration was this question, but also did not worked (see my code example). If I sort/filter, in console output I can see numbers of painted rows i while creating PaintListener, but row heights are not updated.
Creating table:
public class NatTableFactory {
// another code
private NatTable createTable(Composite parent, List<TableLine> tLines, String[][] propertyNames,
PropertyToLabels[] propToLabels, TableParams params, TextMatcherEditor<TableLine>editor, boolean openableParts) {
BodyLayerStack bodyLayerStack =
new BodyLayerStack(
tLines,
tLines.get(0).getLength(),
params.getColumnIndicesForRowHeaders());
DataLayer bodyDataLayer = bodyLayerStack.getBodyDataLayer();
Integer[] rowHeights = getRowHeights(params);
if( rowHeights != null && rowHeights.length > 0 ) {
for( int i = 0; i < rowHeights.length; i++ ) {
if( rowHeights[i] != null )
bodyDataLayer.setRowHeightByPosition(i, (int) (rowHeights[i] * ApplicationSizeManager.getRowSizeCoefficient()));
}
}
Integer[] colWidths = getColumnWidths(params);
if( colWidths != null && colWidths.length > 0 ) {
for( int i = 0; i < colWidths.length; i++ )
if( colWidths[i] != null )
bodyDataLayer.setColumnWidthByPosition(i, colWidths[i]);
}
DataLayer columnHeaderDataLayer = null;
if( propertyNames != null ) {
IDataProvider columnHeaderDataProvider =
new DefaultColumnHeaderDataProvider(propertyNames[0], propToLabels[0].getPropertyToLabels());
columnHeaderDataLayer =
new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
}
//copy command: include table headers
bodyLayerStack.registerCommandHandler(new CustomCopyDataCommandHandler(bodyLayerStack.getSelectionLayer(), columnHeaderDataLayer, null, params.getColumnHeaderGroups()));
CompositeLayer composite = null;
if( propertyNames != null ) {
ColumnHeaderLayer columnHeaderLayer =
new ColumnHeaderLayer(
columnHeaderDataLayer,
bodyLayerStack,
(SelectionLayer)null);
columnHeaderLayer.addConfiguration(NatTableLayerConfigurations.getColumnHeaderLayerConfiguration(false));
SortHeaderLayer<TableLine> sortHeaderLayer =
new SortHeaderLayer<TableLine>(
columnHeaderLayer,
new GlazedListsSortModel<TableLine>(
bodyLayerStack.getSortedList(),
getSortingColumnPropAccessor(propertyNames[0]),
configRegistry,
columnHeaderDataLayer));
Integer[] hrHeights = params.getHeaderRowHeights();
if( hrHeights != null && hrHeights.length > 0 )
columnHeaderDataLayer.setRowHeightByPosition(0, (int) (hrHeights[0] * ApplicationSizeManager.getRowSizeCoefficient()));
composite = new CompositeLayer(1, 2);
composite.setChildLayer(GridRegion.COLUMN_HEADER, sortHeaderLayer, 0, 0);
composite.setChildLayer(GridRegion.BODY, bodyLayerStack, 0, 1);
} else {
composite = new CompositeLayer(1, 1);
composite.setChildLayer(GridRegion.BODY, bodyLayerStack, 0, 0);
}
composite.addConfiguration(NatTableLayerConfigurations.getCompoositeLayerConfiguration());
NatTable natTable = new NatTable(parent, composite, false);
if( params.getAutoFitColWidthIndices().size() > 0 )
registerAutoResizeColCmdHandler(natTable, composite, bodyLayerStack, params.getAutoFitColWidthIndices());
// register AutoResizeRowCommandHandler
// something wrong here?
registerAutoResizeRowCommandHandler(natTable, composite, bodyLayerStack, params.getAutoFitColWidthIndices());
setNatTableContentTooltip(natTable);
natTable.setConfigRegistry(configRegistry);
natTable.addConfiguration(new SingleClickSortConfiguration());
if(columnHeaderDataLayer!=null)
natTable.addConfiguration(NatTableLayerConfigurations.getCustomComparatorConfiguration(columnHeaderDataLayer));
natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
setNatTableContextMenu(natTable, openableParts);
natTable.addConfiguration(NatTableLayerConfigurations.getNatTableConfiguration());
//
/*
natTable.addConfiguration(new DefaultNatTableStyleConfiguration()
{
{
cellPainter = new LineBorderDecorator(new AutomaticRowHeightTextPainter(5));
}
});*/
natTable.configure();
editor.setFilterator(new TextFilterator<TableLine>() {
#Override
public void getFilterStrings(List<String> baseList, TableLine element) {
for( int i = 0; i < element.getLength(); i++ )
baseList.add("" + element.getObjectByColumnForFilter(i));
}
});
editor.setMode(TextMatcherEditor.REGULAR_EXPRESSION);
bodyLayerStack.getFilterList().setMatcherEditor(editor);
NatTableContentProvider.addNatTableData(natTable, bodyLayerStack.getSelectionLayer(), bodyLayerStack.getBodyDataProvider());
return natTable;
}
// another code
}
register AutoResizeRowCommandHandler:
// in class NatTableFactory
private void registerAutoResizeRowCommandHandler(NatTable natTable, ILayer commandLayer, ILayer positionLayer, List<Integer> colPositions)
{
natTable.registerCommandHandler(new AutoResizeRowCommandHandler(commandLayer, positionLayer));
PaintListener listener = new PaintListener()
{
#Override
public void paintControl(PaintEvent e)
{
System.out.println("paintControl");
for (int i = 0; i < natTable.getRowCount(); i++)
{
System.out.println(i);
InitializeAutoResizeRowsCommand rowCommand = new InitializeAutoResizeRowsCommand(natTable, i, natTable.getConfigRegistry(), new GCFactory(
natTable));
AutoResizeRowsCommand command = new AutoResizeRowsCommand(rowCommand);
natTable.doCommand(command);
}
}
};
natTable.addPaintListener(listener);
}
BodyLayerStack:
public BodyLayerStack(List<TableLine> values, int columnCount, Integer[] columnIndicesForRowHeaders)
{
EventList<TableLine> eventList = GlazedLists.eventList(values);
TransformedList<TableLine, TableLine> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
// this.filterList = new FilterList<>(rowObjectsGlazedList); //changed to:
// use the SortedList constructor with 'null' for the Comparator
// because the Comparator will be set by configuration
sortedList = new SortedList<>(rowObjectsGlazedList, null);
// wrap the SortedList with the FilterList
filterList = new FilterList<>(sortedList);
bodyDataProvider = new ListDataProvider<TableLine>(filterList, getColumnAccessor(columnCount));
bodyDataLayer = new DataLayer(bodyDataProvider);
IConfigLabelAccumulator cellLabelAccumulator = new IConfigLabelAccumulator() {
#Override
public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
int columnIndex = bodyDataLayer.getColumnIndexByPosition(columnPosition);
int rowIndex = bodyDataLayer.getRowIndexByPosition(rowPosition);
if(isRowHeader(columnIndicesForRowHeaders, columnIndex))
configLabels.addLabel(NatTableFactory.RowHeaderLabel);
else
{
// get dataTypes at actual positions and add/refresh labels
String label = filterList.get(rowIndex).getObjectTypeByColumn(columnIndex);
// adjust label according to autoFitWitdh option
if(filterList.get(rowIndex).isAutoFitColWidth(columnIndex))
label += "_" + NatTableFactory.AutoFitWidthLabel;
else
label += "_" + NatTableFactory.NoAutoFitWidthLabel;
configLabels.addLabel(label);
}
}
};
bodyDataLayer.setConfigLabelAccumulator(cellLabelAccumulator);
GlazedListsEventLayer<TableLine> glazedListsEventLayer = new GlazedListsEventLayer<>(bodyDataLayer, filterList);
selectionLayer = new SelectionLayer(glazedListsEventLayer, false);
selectionLayer.setSelectionModel(getSelectionModel());
selectionLayer.addConfiguration(getSelectionConfiguration());
//
viewportLayer = new ViewportLayer(selectionLayer);
setUnderlyingLayer(new ViewportLayer(selectionLayer));
}
If I call natTable.doCommand(rowCommand) instead of natTable.doCommand(command) while registering handler, row heights are updated correctly, but table is painted repetitively, scroll bar blinks all the time.
I tried also:
private void registerAutoResizeRowCommandHandler(NatTable natTable, ILayer commandLayer, ILayer positionLayer, List<Integer> colPositions)
{
natTable.registerCommandHandler(new AutoResizeRowCommandHandler(commandLayer, positionLayer));
BodyLayerStack bodyLayerStack = (BodyLayerStack) positionLayer;
natTable.addPaintListener(
new AutoResizeRowPaintListener(
natTable,
bodyLayerStack.getViewportLayer(),
bodyLayerStack.getBodyDataLayer()));
}
Any idea what did I miss?
Well for a complete answer I would need quite some time to explain the internals of NatTable. Not sure if you are really interested in this. So I simply answer how to do it right.
With NatTable 1.6 the AutoResizeRowPaintListener was added to simplify the auto row resize task. Information about this can be found here: https://www.eclipse.org/nattable/nandn/nandn_160.php
I have got it:
in NatTableFactory:
private NatTable createTable(Composite parent, List<TableLine> tLines, String[][] propertyNames,
PropertyToLabels[] propToLabels, TableParams params, TextMatcherEditor<TableLine>editor,
boolean openableParts)
{
// another code
registerAutoResizeRowCommandHandler(natTable, composite, bodyLayerStack);
//another code
return natTable;
}
and:
private void registerAutoResizeRowCommandHandler(NatTable natTable,
CompositeLayer commandLayer, BodyLayerStack bodyLayerStack)
{
natTable.registerCommandHandler(new AutoResizeRowCommandHandler(commandLayer, bodyLayerStack));
natTable.addPaintListener(
new AutoResizeRowPaintListener(
natTable,
bodyLayerStack.getViewportLayer(),
bodyLayerStack.getBodyDataLayer()));
}
My mistake was in BodyLayerStack:
viewportLayer = new ViewportLayer(selectionLayer);
setUnderlyingLayer(new ViewportLayer(selectionLayer));
correct is:
viewportLayer = new ViewportLayer(selectionLayer);
setUnderlyingLayer(viewportLayer);
Thanks Dirk Fauth!

How to setSelected by name some JMenuItem in a JPopUpMenu?

I have a JPopUpMenu with several JCheckBoxMenuItem's on it.
I also have a properties files (config.properties) where I put a parameter which save every JCheckBoxMenuItem the user has checked on this JPopUpMenu , when i close the application.
So this file is like :
config.properties :
listeFiltres =Autres,Afrique du sud,Algérie
What I would like to do is , on the start of my application, to setSelected every item that is saved in my properties parameter.
For exemple, if "Afrique,Algérie,Allemagne" are stored in my parameter listeFiltres in config.properties, I want these 3 JCheckBoxMenuItem to be checked at the start of my application.
The problem is that I only know the setSelected method which allows to select an item with a specific index(like 2), but here I need to select an item with a specific name (like "Afrique"), so this method isn't appropriate for me.
Here's the code of my JPopUpMenu :
MainVue.java:
public class MainVue extends JFrame implements ActionListener {
private static final JScrollPopupMenu menuProduit = new JScrollPopupMenu();
private static final JScrollPopupMenu menuPays = new JScrollPopupMenu();
private static List<String> listeFiltres = new ArrayList<String>();
private String listeDeFiltres;
private String[] tableauFiltrePermanent;
private String listeFiltrePermanent;
private String[] tableauPays = { "Autres", "Afrique du sud", "Algérie", "Allemagne", "Arabie Saoudite", "Argentine",
"Australie", "Bangladesh", "Belgique", "Brésil", "Bulgarie", "Canada", "Chine", "Corée du sud", "Egypte",
"Emirats-Arabes Unis", "Espagne", "Etats-Unis", "Ethiopie", "Europe", "France", "Hongrie", "Inde",
"Indonésie", "Irak", "Iran", "Israél", "Italie", "Japon", "Jordanie", "Kazakhstan", "Koweit", "Liban",
"Libye", "Malaisie", "Maroc", "Mexique", "Monde", "Oman", "Pakistan", "Pays-Bas", "Philippines", "Poligne",
"Portugal", "Qatar", "République tchéque", "Roumanie", "Russie", "Taïwan", "Tunisie", "Turquie",
"Ukraine" };
private String[] tableauProduit = { "Blé", "Colza", "Mais", "Orge", "Orge de Brasserie", "Palme", "Soja",
"Tournesol", "Tourteaux De Colza", "Tourteaux de Soja", "Huile de Soja", "Huile De Colza" };
private List<JCheckBoxMenuItem> listJCBProduit = new ArrayList<JCheckBoxMenuItem>();
private List<JCheckBoxMenuItem> listJCBPays = new ArrayList<JCheckBoxMenuItem>();
public static PropertiesConfiguration prop;
public MainVue(Modele modele, Controleur controleur) throws ClassNotFoundException, SQLException, IOException {
prop = new PropertiesConfiguration("config.properties");
for (int i = 0; i < tableauProduit.length; i++) {
listJCBProduit.add(new JCheckBoxMenuItem(tableauProduit[i]));
}
for (int j = 0; j < listJCBProduit.size(); j++) {
JCheckBoxMenuItem produitActuel = listJCBProduit.get(j);
menuProduit.add(produitActuel);
produitActuel.addActionListener(new OpenAction(menuProduit, boutonProduit));
}
for (int i = 0; i < tableauPays.length; i++) {
listJCBPays.add(new JCheckBoxMenuItem(tableauPays[i]));
}
for (int j = 0; j < listJCBPays.size(); j++) {
JCheckBoxMenuItem paysActuel = listJCBPays.get(j);
menuPays.add(paysActuel);
paysActuel.addActionListener(new OpenAction(menuPays, boutonPays));
}
listeDeFiltres = "";
for (int p = 0; p < listeFiltres.size(); p++) {
String filtreActuel = listeFiltres.get(p);
if (listeDeFiltres == "") {
listeDeFiltres += filtreActuel;
} else {
listeDeFiltres += "," + filtreActuel;
}
}
prop.setProperty("listeFiltres", listeDeFiltres);
}
}
You can get the index of name from tableauPays array as follows and can pass it to the setSelected() method
public int getIndex(String name){
int index = 0;
for(String p : tableauPays){
if(p.equals(name)){
return index;
}
index++;
}
}

Copying fields in iTextSharp 5.4.5.0

I was under the impression that it is now possible to copy AcroFields using PdfCopy. In the release notes for iText 5.4.4.0 this is listed as possible now. However, when I try to do so it appears all the annotations (I think I am using that term correctly, still fairly new to iText...) for the fields are stripped out. It looks like the fields are there (meaning I can see the blue boxes that indicate an editable field), but they are not editable. If I try to bring the PDF up in Acrobat I get a message saying that "there are no fields, would you like Acrobat to discover them?" and most are found and marked and fields properly (check boxes aren't, but the text fields are).
I assume there is an additional step somewhere along the lines to re-add the annotations to the PdfCopy object, but I do not see a way to get the annotations from the PdfReader. I also cannot seem to find any documentation on how to do this (since AcroFields were for so long not supported in PdfCopy most of what I find is along that vein).
Due to sensitivity I cannot provide a copy of the PDF's in question, but using an altered version of a test program used earlier you can see the issue with the following code. It should generate a table with some check boxes in the four right columns. If I use the exact same code with PdfCopyFields in the MergePdfs method instead of PdfCopy it works as expected. This code does not produce any text fields, but in my main project they are part of the original parent PDF that is used as a template.
(Sorry for the long example, it has been cherry picked from a much larger application. You will need a PDF with a field named "TableStartPosition" somewhere in it and update RunTest with the correct paths for your local machine to get this to work.)
Has the PdfCopy functionality not made it into iTextSharp yet? I am using version 5.4.5.0.
class Program
{
Stream _pdfTemplateStream;
MemoryStream _pdfResultStream;
PdfReader _pdfTemplateReader;
PdfStamper _pdfResultStamper;
static void Main(string[] args)
{
Program p = new Program();
try
{
p.RunTest();
}
catch (Exception f)
{
Console.WriteLine(f.Message);
Console.ReadLine();
}
}
internal void RunTest()
{
FileStream fs = File.OpenRead(#"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\CoverPage.pdf");
_pdfTemplateStream = fs;
_pdfResultStream = new MemoryStream();
//PDFTemplateStream = new FileStream(_templatePath, FileMode.Open);
_pdfTemplateReader = new PdfReader(_pdfTemplateStream);
_pdfResultStamper = new PdfStamper(_pdfTemplateReader, _pdfResultStream);
#region setup objects
List<CustomCategory> Categories = new List<CustomCategory>();
CustomCategory c1 = new CustomCategory();
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[2]);
Categories.Add(c1);
CustomCategory c2 = new CustomCategory();
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[0]);
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
Categories.Add(c2);
List<CustomObject> Items = new List<CustomObject>();
CustomObject co1 = new CustomObject();
co1.Category = c1;
co1.Title = "Object 1";
Items.Add(co1);
CustomObject co2 = new CustomObject();
co2.Category = c2;
co2.Title = "Object 2";
Items.Add(co2);
#endregion
FillCoverPage(Items);
_pdfResultStamper.Close();
_pdfTemplateReader.Close();
List<MemoryStream> pdfStreams = new List<MemoryStream>();
pdfStreams.Add(new MemoryStream(_pdfResultStream.ToArray()));
MergePdfs(#"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\Outfile.pdf", pdfStreams);
_pdfResultStream.Dispose();
_pdfTemplateStream.Dispose();
}
internal void FillCoverPage(List<CustomObject> Items)
{
//Before we start we need to figure out where to start adding the table
var fieldPositions = _pdfResultStamper.AcroFields.GetFieldPositions("TableStartPosition");
if (fieldPositions == null)
{ throw new Exception("Could not find the TableStartPosition field. Unable to determine point of origin for the table!"); }
_pdfResultStamper.AcroFields.RemoveField("TableStartPosition");
var fieldPosition = fieldPositions[0];
// Get the position of the field
var targetPosition = fieldPosition.position;
//First, get all the available card sizes
List<string> availableSizes = CustomCategory.AvailableSizes;
//Generate a table with the number of available card sizes + 1 for the device name
PdfPTable table = new PdfPTable(availableSizes.Count + 1);
float[] columnWidth = new float[availableSizes.Count + 1];
for (int y = 0; y < columnWidth.Length; y++)
{
if (y == 0)
{ columnWidth[y] = 320; }
else
{ columnWidth[y] = 120; }
}
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
PdfContentByte canvas;
List<PdfFormField> checkboxes = new List<PdfFormField>();
//Build the header row
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
//Insert the global check boxes
PdfPCell[] globalRow = new PdfPCell[availableSizes.Count + 1];
Phrase tPhrase = new Phrase("Select/Unselect All");
PdfPCell tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
tCell.AddElement(tPhrase);
globalRow[0] = tCell;
for (int x = 0; x < availableSizes.Count; x++)
{
tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.Global.chk{0}", availableSizes[x].Replace(".", ""));
//f.FieldName = fieldName;
string js = string.Format("hideAll(event.target, '{0}');", availableSizes[x].Replace(".", ""));
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
globalRow[x + 1] = tCell;
checkboxes.Add(f);
}
table.Rows.Add(new PdfPRow(globalRow));
int status = 0;
int pageNum = 1;
for (int itemIndex = 0; itemIndex < Items.Count; itemIndex++)
{
tCell = new PdfPCell();
Phrase p = new Phrase(Items[itemIndex].Title);
tCell.AddElement(p);
tCell.HorizontalAlignment = Element.ALIGN_LEFT;
PdfPCell[] cells = new PdfPCell[availableSizes.Count + 1];
cells[0] = tCell;
for (int availCardSizeIndex = 0; availCardSizeIndex < availableSizes.Count; availCardSizeIndex++)
{
if (Items[itemIndex].Category.CategorySizesInUse.Contains(availableSizes[availCardSizeIndex]))
{
string str = availableSizes[availCardSizeIndex];
tCell = new PdfPCell();
tCell.PaddingLeft = 10f;
tCell.PaddingRight = 10f;
cells[availCardSizeIndex + 1] = tCell;
cells[availCardSizeIndex].HorizontalAlignment = Element.ALIGN_CENTER;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.chk{0}.{1}", availableSizes[availCardSizeIndex].Replace(".", ""), itemIndex + 1);
//f.FieldName = fieldName; <-- This causes the checkbox to be double-named (i.e. InkSaver.Global.chk0.InkSaver.Global.chk0
string js = string.Format("hideCardSize(event.target, {0}, '{1}');", itemIndex + 1, availableSizes[availCardSizeIndex]);
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
checkboxes.Add(f);
}
else
{
//Add a blank cell
tCell = new PdfPCell();
cells[availCardSizeIndex + 1] = tCell;
}
}
//Test if the column text will fit
table.Rows.Add(new PdfPRow(cells));
canvas = _pdfResultStamper.GetUnderContent(pageNum);
ColumnText ct2 = new ColumnText(canvas);
ct2.AddElement(new PdfPTable(table));
ct2.Alignment = Element.ALIGN_LEFT;
ct2.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
status = ct2.Go(true);
if ((status != ColumnText.NO_MORE_TEXT) || (itemIndex == (Items.Count - 1)))
{
ColumnText ct3 = new ColumnText(canvas);
ct3.AddElement(table);
ct3.Alignment = Element.ALIGN_LEFT;
ct3.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
ct3.Go();
foreach (PdfFormField f in checkboxes)
{
_pdfResultStamper.AddAnnotation(f, pageNum);
}
checkboxes.Clear();
if (itemIndex < (Items.Count - 1))
{
pageNum++;
_pdfResultStamper.InsertPage(pageNum, _pdfTemplateReader.GetPageSize(1));
table = new PdfPTable(availableSizes.Count + 1);
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
}
}
}
}
private PdfPCell[] GetTableHeaderRow(List<string> AvailableSizes)
{
PdfPCell[] sizeHeaders = new PdfPCell[AvailableSizes.Count + 1];
Phrase devName = new Phrase("Device Name");
PdfPCell deviceHeader = new PdfPCell(devName);
deviceHeader.HorizontalAlignment = Element.ALIGN_CENTER;
deviceHeader.BackgroundColor = BaseColor.GRAY;
sizeHeaders[0] = deviceHeader;
for (int x = 0; x < AvailableSizes.Count; x++)
{
PdfPCell hCell = new PdfPCell(new Phrase(AvailableSizes[x]));
hCell.HorizontalAlignment = Element.ALIGN_CENTER;
hCell.BackgroundColor = BaseColor.GRAY;
sizeHeaders[x + 1] = hCell;
}
return sizeHeaders;
}
public void MergePdfs(string filePath, List<MemoryStream> pdfStreams)
{
//Create output stream
FileStream outStream = new FileStream(filePath, FileMode.Create);
Document document = null;
if (pdfStreams.Count > 0)
{
try
{
int PageCounter = 0;
//Create Main reader
PdfReader reader = new PdfReader(pdfStreams[0]);
PageCounter = reader.NumberOfPages;//This is if we have multiple pages in the cover page, we need to adjust the offset.
//rename fields in the PDF. This is required because PDF's cannot have more than one field with the same name
RenameFields(reader, PageCounter++);
//Create Main Doc
document = new Document(reader.GetPageSizeWithRotation(1));
//Create main writer
PdfCopy Writer = new PdfCopy(document, outStream);
//PdfCopyFields Writer = new PdfCopyFields(outStream);
//Open document for writing
document.Open();
////Add pages
Writer.AddDocument(reader);
//For each additional pdf after first combine them into main document
foreach (var PdfStream in pdfStreams.Skip(1))
{
PdfReader reader2 = new PdfReader(PdfStream);
//rename PDF fields
RenameFields(reader2, PageCounter++);
// Add content
Writer.AddDocument(reader);
}
//Writer.AddJavaScript(PostProcessing.GetSuperscriptJavaScript());
Writer.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (document != null)
document.Close();
foreach (var Strm in pdfStreams)
{
try { if (null != Strm) Strm.Dispose(); }
catch { }
}
//pdfStamper.Close();
outStream.Close();
}
}
}
private void RenameFields(PdfReader reader, int PageNum)
{
int tempPageNum = 1;
//rename all fields
foreach (string field in reader.AcroFields.Fields.Keys)
{
if (((reader.AcroFields.GetFieldType(field) == 1) || (reader.AcroFields.GetFieldType(field) == 2)) && (field.StartsWith("InkSaver")))
{
//This is a InkSaver button, set the name so its subclassed
string classPath;
if (reader.AcroFields.GetFieldType(field) == 2)
{
classPath = field.Substring(0, field.LastIndexOf("."));
if (field.StartsWith("InkSaver.chk"))
{
int a = field.LastIndexOf(".");
string sub = field.Substring(a + 1, (field.Length - a - 1));
int pageNum = int.Parse(sub);
int realPageNum = pageNum + tempPageNum;//PostProcessing.Instance.CoverPageLength;
PageNum = realPageNum;
}
}
else
{
classPath = field.Substring(0, field.LastIndexOf("."));
}
string newID = classPath + ".page" + PageNum.ToString();
bool ret = reader.AcroFields.RenameField(field, newID);
}
else
{
reader.AcroFields.RenameField(field, field + "_" + PageNum.ToString());// field + Guid.NewGuid().ToString("N"));
}
}
}
}
public class ChildFieldEvent : IPdfPCellEvent
{
protected PdfWriter writer;
protected PdfFormField parent;
protected string checkBoxName;
internal ChildFieldEvent(PdfWriter writer, PdfFormField parent, string CheckBoxName)
{
this.writer = writer;
this.parent = parent;
this.checkBoxName = CheckBoxName;
}
public void CellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] cb)
{
createCheckboxField(rect);
}
private void createCheckboxField(Rectangle rect)
{
RadioCheckField bt = new RadioCheckField(this.writer, rect, this.checkBoxName, "Yes");
bt.CheckType = RadioCheckField.TYPE_SQUARE;
bt.Checked = true;
this.parent.AddKid(bt.CheckField);
}
}
internal class CustomCategory
{
internal static List<string> AvailableSizes
{
get
{
List<string> retVal = new List<string>();
retVal.Add("1");
retVal.Add("2");
retVal.Add("3");
retVal.Add("4");
return retVal;
}
}
internal CustomCategory()
{
CategorySizesInUse = new List<string>();
}
internal List<string> CategorySizesInUse { get; set; }
}
internal class CustomObject
{
internal string Title { get; set; }
internal CustomCategory Category { get;set; }
}
Please take a look at the MergeForms example. Your example is too long for me to read, but at first sight, I'm missing the following line:
copy.setMergeFields();
By the way, in MergeForms2, the fields are also renamed before the form is merged.

How to implement boolean retrieval using hitcollector in below scenario

I am running my code on TREC documents and right now implementing scoring scheme to get number of relevant documents. However now i want to implement boolean retrieval, I am trying to use HitCollector.
below is my code..
public class BatchSearch {
private BatchSearch() {}
/** Simple command-line based search demo. */
public static void main(String[] args) throws Exception {
String usage =
"Usage:\tjava BatchSearch [-index dir] [-simfn similarity] [-field f] [-queries file]";
if (args.length > 0 && ("-h".equals(args[0]) || "-help".equals(args[0]))) {
System.out.println(usage);
System.out.println("Supported similarity functions:\ndefault: DefaultSimilary (tfidf)\n");
System.exit(0);
}
String index = "index";
String field = "contents";
String queries = null;
String simstring = "default";
for(int i = 0;i < args.length;i++) {
if ("-index".equals(args[i])) {
index = args[i+1];
i++;
} else if ("-field".equals(args[i])) {
field = args[i+1];
i++;
} else if ("-queries".equals(args[i])) {
queries = args[i+1];
i++;
} else if ("-simfn".equals(args[i])) {
simstring = args[i+1];
i++;
}
}
Similarity simfn = null;
if ("default".equals(simstring)) {
simfn = new DefaultSimilarity();
} else if ("bm25".equals(simstring)) {
simfn = new BM25Similarity();
} else if ("dfr".equals(simstring)) {
simfn = new DFRSimilarity(new BasicModelP(), new AfterEffectL(), new NormalizationH2());
} else if ("lm".equals(simstring)) {
simfn = new LMDirichletSimilarity();
}
if (simfn == null) {
System.out.println(usage);
System.out.println("Supported similarity functions:\ndefault: DefaultSimilary (tfidf)");
System.out.println("bm25: BM25Similarity (standard parameters)");
System.out.println("dfr: Divergence from Randomness model (PL2 variant)");
System.out.println("lm: Language model, Dirichlet smoothing");
System.exit(0);
}
IndexReader reader = DirectoryReader.open(FSDirectory.open(new File(index)));
IndexSearcher searcher = new IndexSearcher(reader);
searcher.setSimilarity(simfn);
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_41);
BufferedReader in = null;
if (queries != null) {
in = new BufferedReader(new InputStreamReader(new FileInputStream(queries), "UTF-8"));
} else {
in = new BufferedReader(new InputStreamReader(new FileInputStream("queries"), "UTF-8"));
}
QueryParser parser = new QueryParser(Version.LUCENE_41, field, analyzer);
while (true) {
String line = in.readLine();
if (line == null || line.length() == -1) {
break;
}
line = line.trim();
if (line.length() == 0) {
break;
}
String[] pair = line.split(" ", 2);
Query query = parser.parse(pair[1]);
doBatchSearch(in, searcher, pair[0], query, simstring);
}
reader.close();
}
/**
* This function performs a top-1000 search for the query as a basic TREC run.
*/
public static void doBatchSearch(BufferedReader in, IndexSearcher searcher, String qid, Query query, String runtag)
throws IOException {
// Collect enough docs to show 5 pages
TopDocs results = searcher.search(query, 1000);
ScoreDoc[] hits = results.scoreDocs;
HashMap<String, String> seen = new HashMap<String, String>(1000);
int numTotalHits = results.totalHits;
int start = 0;
int end = Math.min(numTotalHits, 1000);
for (int i = start; i < end; i++) {
Document doc = searcher.doc(hits[i].doc);
String docno = doc.get("docno");
// There are duplicate document numbers in the FR collection, so only output a given
// docno once.
if (seen.containsKey(docno)) {
continue;
}
seen.put(docno, docno);
System.out.println(qid+" Q0 "+docno+" "+i+" "+hits[i].score+" "+runtag);
}
}
}
The scoring is done in doBatchSearch and now i want to implement HitCollector here.

Can I drag items from Outlook into my SWT application?

Background
Our Eclipse RCP 3.6-based application lets people drag files in for storage/processing. This works fine when the files are dragged from a filesystem, but not when people drag items (messages or attachments) directly from Outlook.
This appears to be because Outlook wants to feed our application the files via a FileGroupDescriptorW and FileContents, but SWT only includes a FileTransfer type. (In a FileTransfer, only the file paths are passed, with the assumption that the receiver can locate and read them. The FileGroupDescriptorW/FileContents approach can supply files directly application-to-application without writing temporary files out to disk.)
We have tried to produce a ByteArrayTransfer subclass that could accept FileGroupDescriptorW and FileContents. Based on some examples on the Web, we were able to receive and parse the FileGroupDescriptorW, which (as the name implies) describes the files available for transfer. (See code sketch below.) But we have been unable to accept the FileContents.
This seems to be because Outlook offers the FileContents data only as TYMED_ISTREAM or TYMED_ISTORAGE, but SWT only understands how to exchange data as TYMED_HGLOBAL. Of those, it appears that TYMED_ISTORAGE would be preferable, since it's not clear how TYMED_ISTREAM could provide access to multiple files' contents.
(We also have some concerns about SWT's desire to pick and convert only a single TransferData type, given that we need to process two, but we think we could probably hack around that in Java somehow: it seems that all the TransferDatas are available at other points of the process.)
Questions
Are we on the right track here? Has anyone managed to accept FileContents in SWT yet? Is there any chance that we could process the TYMED_ISTORAGE data without leaving Java (even if by creating a fragment-based patch to, or a derived version of, SWT), or would we have to build some new native support code too?
Relevant code snippets
Sketch code that extracts file names:
// THIS IS NOT PRODUCTION-QUALITY CODE - FOR ILLUSTRATION ONLY
final Transfer transfer = new ByteArrayTransfer() {
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
if (!isSupportedType(transferData))
return null;
final byte[] buffer = (byte[]) super.nativeToJava(transferData);
if (buffer == null)
return null;
try {
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer));
long count = 0;
for (int i = 0; i < 4; i++) {
count += in.readUnsignedByte() << i;
}
for (int i = 0; i < count; i++) {
final byte[] filenameBytes = new byte[260 * 2];
in.skipBytes(72); // probable architecture assumption(s) - may be wrong outside standard 32-bit Win XP
in.read(filenameBytes);
final String fileNameIncludingTrailingNulls = new String(filenameBytes, "UTF-16LE");
int stringLength = fileNameIncludingTrailingNulls.indexOf('\0');
if (stringLength == -1)
stringLength = 260;
final String fileName = fileNameIncludingTrailingNulls.substring(0, stringLength);
System.out.println("File " + i + ": " + fileName);
}
in.close();
return buffer;
}
catch (final Exception e) {
return null;
}
}
};
In the debugger, we see that ByteArrayTransfer's isSupportedType() ultimately returns false for the FileContents because the following test is not passed (since its tymed is TYMED_ISTREAM | TYMED_ISTORAGE):
if (format.cfFormat == types[i] &&
(format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT &&
(format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL )
return true;
This excerpt from org.eclipse.swt.internal.ole.win32.COM leaves us feeling less hope for an easy solution:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
Thanks.
even if
//public static final int TYMED_ISTREAM = 4;
Try below code.. it should work
package com.nagarro.jsag.poc.swtdrag;
imports ...
public class MyTransfer extends ByteArrayTransfer {
private static int BYTES_COUNT = 592;
private static int SKIP_BYTES = 72;
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
String[] result = null;
if (!isSupportedType(transferData) || transferData.pIDataObject == 0)
return null;
IDataObject data = new IDataObject(transferData.pIDataObject);
data.AddRef();
// Check for descriptor format type
try {
FORMATETC formatetcFD = transferData.formatetc;
STGMEDIUM stgmediumFD = new STGMEDIUM();
stgmediumFD.tymed = COM.TYMED_HGLOBAL;
transferData.result = data.GetData(formatetcFD, stgmediumFD);
if (transferData.result == COM.S_OK) {
// Check for contents format type
long hMem = stgmediumFD.unionField;
long fileDiscriptorPtr = OS.GlobalLock(hMem);
int[] fileCount = new int[1];
try {
OS.MoveMemory(fileCount, fileDiscriptorPtr, 4);
fileDiscriptorPtr += 4;
result = new String[fileCount[0]];
for (int i = 0; i < fileCount[0]; i++) {
String fileName = handleFile(fileDiscriptorPtr, data);
System.out.println("FileName : = " + fileName);
result[i] = fileName;
fileDiscriptorPtr += BYTES_COUNT;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
OS.GlobalFree(hMem);
}
}
} finally {
data.Release();
}
return result;
}
private String handleFile(long fileDiscriptorPtr, IDataObject data) throws Exception {
// GetFileName
char[] fileNameChars = new char[OS.MAX_PATH];
byte[] fileNameBytes = new byte[OS.MAX_PATH];
COM.MoveMemory(fileNameBytes, fileDiscriptorPtr, BYTES_COUNT);
// Skip some bytes.
fileNameBytes = Arrays.copyOfRange(fileNameBytes, SKIP_BYTES, fileNameBytes.length);
String fileNameIncludingTrailingNulls = new String(fileNameBytes, "UTF-16LE");
fileNameChars = fileNameIncludingTrailingNulls.toCharArray();
StringBuilder builder = new StringBuilder(OS.MAX_PATH);
for (int i = 0; fileNameChars[i] != 0 && i < fileNameChars.length; i++) {
builder.append(fileNameChars[i]);
}
String name = builder.toString();
try {
File file = saveFileContent(name, data);
if (file != null) {
System.out.println("File Saved # " + file.getAbsolutePath());
;
}
} catch (IOException e) {
System.out.println("Count not save file content");
;
}
return name;
}
private File saveFileContent(String fileName, IDataObject data) throws IOException {
File file = null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = typeIds[1];
formatetc.dwAspect = COM.DVASPECT_CONTENT;
formatetc.lindex = 0;
formatetc.tymed = 4; // content.
STGMEDIUM stgmedium = new STGMEDIUM();
stgmedium.tymed = 4;
if (data.GetData(formatetc, stgmedium) == COM.S_OK) {
file = new File(fileName);
IStream iStream = new IStream(stgmedium.unionField);
iStream.AddRef();
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int increment = 1024 * 4;
long pv = COM.CoTaskMemAlloc(increment);
int[] pcbWritten = new int[1];
while (iStream.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
byte[] buffer = new byte[pcbWritten[0]];
OS.MoveMemory(buffer, pv, pcbWritten[0]);
outputStream.write(buffer);
}
COM.CoTaskMemFree(pv);
} finally {
iStream.Release();
}
return file;
} else {
return null;
}
}
}
Have you looked at https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514 ?
Attached to this bugzilla entry is an patch (against an rather old version of SWT) that might be of interest.
I had the same problem and created a small library providing a Drag'n Drop Transfer Class for JAVA SWT. It can be found here:
https://github.com/HendrikHoetker/OutlookItemTransfer
Currently it supports dropping Mail Items from Outlook to your Java SWT application and will provide a list of OutlookItems with the Filename and a byte array of the file contents.
All is pure Java and in-memory (no temp files).
Usage in your SWT java application:
if (OutlookItemTransfer.getInstance().isSupportedType(event.currentDataType)) {
Object o = OutlookItemTransfer.getInstance().nativeToJava(event.currentDataType);
if (o != null && o instanceof OutlookMessage[]) {
OutlookMessage[] outlookMessages = (OutlookMessage[])o;
for (OutlookMessage msg: outlookMessages) {
//...
}
}
}
The OutlookItem will then provide two elements: filename as String and file contents as array of byte.
From here on, one could write it to a file or further process the byte array.
To your question above:
- What you find in the file descriptor is the filename of the outlook item and a pointer to an IDataObject
- the IDataObject can be parsed and will provide an IStorage object
- The IStorageObject will be then a root container providing further sub-IStorageObjects or IStreams similar to a filesystem (directory = IStorage, file = IStream
You find those elements in the following lines of code:
Get File Contents, see OutlookItemTransfer.java, method nativeToJava:
FORMATETC format = new FORMATETC();
format.cfFormat = getTypeIds()[1];
format.dwAspect = COM.DVASPECT_CONTENT;
format.lindex = <fileIndex>;
format.ptd = 0;
format.tymed = TYMED_ISTORAGE | TYMED_ISTREAM | COM.TYMED_HGLOBAL;
STGMEDIUM medium = new STGMEDIUM();
if (data.GetData(format, medium) == COM.S_OK) {
// medium.tymed will now contain TYMED_ISTORAGE
// in medium.unionfield you will find the root IStorage
}
Read the root IStorage, see CompoundStorage, method readOutlookStorage:
// open IStorage object
IStorage storage = new IStorage(pIStorage);
storage.AddRef();
// walk through the content of the IStorage object
long[] pEnumStorage = new long[1];
if (storage.EnumElements(0, 0, 0, pEnumStorage) == COM.S_OK) {
// get storage iterator
IEnumSTATSTG enumStorage = new IEnumSTATSTG(pEnumStorage[0]);
enumStorage.AddRef();
enumStorage.Reset();
// prepare statstg structure which tells about the object found by the iterator
long pSTATSTG = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, STATSTG.sizeof);
int[] fetched = new int[1];
while (enumStorage.Next(1, pSTATSTG, fetched) == COM.S_OK && fetched[0] == 1) {
// get the description of the the object found
STATSTG statstg = new STATSTG();
COM.MoveMemory(statstg, pSTATSTG, STATSTG.sizeof);
// get the name of the object found
String name = readPWCSName(statstg);
// depending on type of object
switch (statstg.type) {
case COM.STGTY_STREAM: { // load an IStream (=File)
long[] pIStream = new long[1];
// get the pointer to the IStream
if (storage.OpenStream(name, 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, pIStream) == COM.S_OK) {
// load the IStream
}
}
case COM.STGTY_STORAGE: { // load an IStorage (=SubDirectory) - requires recursion to traverse the sub dies
}
}
}
}
// close the iterator
enumStorage.Release();
}
// close the IStorage object
storage.Release();