Sunday, June 20, 2010

Creating a Smart Tag

So far I have only been able to get the recognize portion of the smart tag to work. The code for the working portion is here:
package com.example;

import com.sun.star.container.XStringKeyMap;
import com.sun.star.i18n.Boundary;
import com.sun.star.i18n.WordType;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lib.uno.helper.Factory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.text.TextMarkupType;


public final class NewSmartTag extends WeakBase
implements com.sun.star.smarttags.XSmartTagRecognizer,
com.sun.star.lang.XServiceInfo
{
private final XComponentContext m_xContext;
private static final String m_SmartTagType = "com.example#newsmarttag";
private static final String m_implementationName = NewSmartTag.class.getName();
private static final String[] m_serviceNames = {
"com.sun.star.smarttags.SmartTagRecognizer" };


public NewSmartTag( XComponentContext context )
{
m_xContext = context;
};

public static XSingleComponentFactory __getComponentFactory( String sImplementationName ) {
XSingleComponentFactory xFactory = null;

if ( sImplementationName.equals( m_implementationName ) )
xFactory = Factory.createComponentFactory(NewSmartTag.class, m_serviceNames);
return xFactory;
}

public static boolean __writeRegistryServiceInfo( XRegistryKey xRegistryKey ) {
return Factory.writeRegistryServiceInfo(m_implementationName,
m_serviceNames,
xRegistryKey);
}

// com.sun.star.lang.XInitialization:
public void initialize(Object[] aArguments) throws com.sun.star.uno.Exception
{
// TODO: Insert your implementation for "initialize" here.
}

// com.sun.star.smarttags.XSmartTagRecognizer:
public int getSmartTagCount()
{
return 1;
}

public String getName(com.sun.star.lang.Locale aLocale)
{
// TODO: Exchange the default return implementation for "getName" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return "Test Smart Tag";
}

public String getDescription(com.sun.star.lang.Locale aLocale)
{
// TODO: Exchange the default return implementation for "getDescription" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return "Testing Smart Tags";
}

public String getSmartTagName(int nSmartTagIndex) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "getSmartTagName" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return m_SmartTagType;
}

public String getSmartTagDownloadURL(int nSmartTagIndex) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "getSmartTagDownloadURL" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return new String();
}

public void recognize(String aText, int nStart, int nLength, com.sun.star.smarttags.SmartTagRecognizerMode eDataType, com.sun.star.lang.Locale aLocale, com.sun.star.text.XTextMarkup xTextMarkup, String aApplicationName, com.sun.star.frame.XController xController, com.sun.star.i18n.XBreakIterator xTokenizer)
{
final int nEndPos = nStart + nLength;
Boundary aWordBounds = xTokenizer.getWordBoundary( aText, nStart, aLocale, WordType.DICTIONARY_WORD, true );
while (aWordBounds.startPos < aWordBounds.endPos && aWordBounds.endPos <=nEndPos)
{
final String aWord = aText.substring(aWordBounds.startPos, aWordBounds.endPos);
if(aWord.equalsIgnoreCase("David"))
{
XStringKeyMap xProps = null;
xTextMarkup.commitTextMarkup(TextMarkupType.SMARTTAG,
m_SmartTagType,
aWordBounds.startPos,
aWordBounds.endPos = aWordBounds.startPos,
xProps);

}
aWordBounds = xTokenizer.nextWord( aText, aWordBounds.startPos, aLocale, WordType.DICTIONARY_WORD );
}
}

public boolean hasPropertyPage(int nSmartTagIndex, com.sun.star.lang.Locale aLocale) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "hasPropertyPage" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return false;
}

public void displayPropertyPage(int nSmartTagIndex, com.sun.star.lang.Locale aLocale) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Insert your implementation for "displayPropertyPage" here.
}

// com.sun.star.lang.XServiceInfo:
public String getImplementationName() {
return m_implementationName;
}

public boolean supportsService( String sService ) {
int len = m_serviceNames.length;

for( int i=0; i < len; i++) {
if (sService.equals(m_serviceNames[i]))
return true;
}
return false;
}

public String[] getSupportedServiceNames() {
return m_serviceNames;
}

}

The recognize function is pretty much the only part that is coded.
First we define the length of the document, then tokenize that string dividing each word. We then set each word as our test and compare it to our string (in this case it is "David" but we could easily replace this with a method with regular expressions. We then set that text to the markup if it matches and then we get the next word. Getting the next word is important because it will give you an infinite loop if you do not.

The other methods are mostly definition methods such as getName, or getDescription and no coding is required to create them.

Sunday, June 13, 2010

Automating OO From .NET

This was an interesting experience, I am a lot more comfortable developing in visual studio but for OO Netbeans has been much easier.

Here is the source:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
using Microsoft.Win32;
using System.IO;
using System.Runtime.InteropServices;
using unoidl.com.sun.star.frame;


namespace ConsoleApplication1
{
class TestOO
{
public int run()
{
InitOpenOfficeEnvironment();
unoidl.com.sun.star.uno.XComponentContext localContent = uno.util.Bootstrap.bootstrap();
unoidl.com.sun.star.lang.XMultiServiceFactory multiServiceFactory = (unoidl.com.sun.star.lang.XMultiServiceFactory)localContent.getServiceManager();
XComponentLoader componentLoader = (XComponentLoader)multiServiceFactory.createInstance("com.sun.star.frame.Desktop");
XComponent xComponent = componentLoader.loadComponentFromURL("private:factory/swriter", "_blank", 0, new unoidl.com.sun.star.beans.PropertyValue[0]);

((unoidl.com.sun.star.text.XTextDocument)xComponent).getText().setString("Automating OO");


return 0;
}
private void InitOpenOfficeEnvironment()
{
string baseKey;
// OpenOffice being a 32 bit app, its registry location is different in a 64 bit OS
if (Marshal.SizeOf(typeof(IntPtr)) == 8)
baseKey = @"SOFTWARE\Wow6432Node\OpenOffice.org\";
else
baseKey = @"SOFTWARE\OpenOffice.org\";

// Get the URE directory
string key = baseKey + @"LayerDev\URE\1";
RegistryKey reg = Registry.CurrentUser.OpenSubKey(key);
if (reg == null) reg = Registry.LocalMachine.OpenSubKey(key);
string urePath = @"C:\Program Files\OOo-dev 3\URE\bin";
reg.Close();
urePath = Path.Combine(urePath, "bin");

// Get the UNO Path
key = baseKey + @"UNO\InstallPath";
reg = Registry.CurrentUser.OpenSubKey(key);
if (reg == null) reg = Registry.LocalMachine.OpenSubKey(key);
string unoPath = @"C:\Program Files\OOo-dev 3\program";
//reg.Close();

string path;
path = string.Format("{0};{1}", System.Environment.GetEnvironmentVariable("PATH"), urePath);
System.Environment.SetEnvironmentVariable("PATH", path);
System.Environment.SetEnvironmentVariable("UNO_PATH", unoPath);
}

}

This is a console app that when runs opens an instance of open office writer and prints "Automating OO".

The InitOpenOfficeEnvironment() method was code that I found here: http://blog.nkadesign.com/2008/net-working-with-openoffice-3/ and it sets up the path and uno_path environment variables so that open office can run. You could also set your paths manually but this allows for distribution. I had a lot of trouble getting the path from the registry as the code was originally for the openoffice 3 and not oo3 dev.

The next part of the code uses the factory style of programming to allow for easier development. It instantiates an openoffice writer and sets the text.