| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| package OfficeDev.samples.Filter; |
| |
| import com.sun.star.lib.uno.helper.Factory; |
| import com.sun.star.lib.uno.helper.WeakBase; |
| import com.sun.star.registry.XRegistryKey; |
| import com.sun.star.uno.XComponentContext; |
| import com.sun.star.lang.XMultiComponentFactory; |
| import com.sun.star.lang.XSingleComponentFactory; |
| import com.sun.star.uno.UnoRuntime; |
| import com.sun.star.uno.Type; |
| import com.sun.star.uno.AnyConverter; |
| import com.sun.star.beans.PropertyValue; |
| |
| import com.sun.star.lang.XInitialization; |
| import com.sun.star.lang.XServiceInfo; |
| import com.sun.star.container.XNamed; |
| import com.sun.star.document.XImporter; |
| import com.sun.star.document.XExporter; |
| import com.sun.star.document.XFilter; |
| |
| |
| import com.sun.star.lang.IllegalArgumentException; |
| |
| /*-************************************************************************ |
| @title implements a filter to import pure ascii text files |
| @description This filter can use an existing In/OutputStream of given |
| MediaDescriptor or use an existing URL instead of that |
| to open the file directly. But for second case the local |
| file system will be used only. There is no support for remote. |
| |
| During import/export special functionality can be used to |
| e.g. to change some characters inside the file content |
| or replace some strings (no using of regular expressions!). |
| User can decide at runtime which functionality realy should |
| be used by selecting it in an extra filter property dialog. |
| |
| So we show how a filter works fro iport/export, use or create |
| streams and how a filter can offer properties for filtering |
| which can be edit by the user. |
| ************************************************************************-*/ |
| |
| public class AsciiReplaceFilter |
| { |
| |
| |
| |
| public static class _AsciiReplaceFilter extends WeakBase |
| implements XInitialization , |
| XServiceInfo , |
| XNamed , |
| XImporter , |
| XExporter , |
| XFilter |
| { |
| //______________________________ |
| // const |
| |
| // the supported service names, the first one being the service name of the component itself |
| public static final String[] m_serviceNames = { "com.sun.star.comp.ansifilter.AsciiReplaceFilter" , "com.sun.star.document.ImportFilter", "com.sun.star.document.ExportFilter" }; |
| |
| // filterprocess states |
| public static final int FILTERPROC_RUNS = 0; |
| public static final int FILTERPROC_BREAK = 1; |
| public static final int FILTERPROC_STOPPED = 2; |
| |
| //______________________________ |
| // member |
| |
| |
| /// The initial component context, that gives access to the service manager, supported singletons, ... |
| private XComponentContext m_Ctx; |
| /// The service manager, that gives access to all registered services and which is passed to the FilterOptions class for instantiating a ucb service |
| private XMultiComponentFactory m_xMCF; |
| /// we must provide our name |
| private String m_sInternalName; |
| /// saved document reference for import or export (depends on other member m_bImport!) |
| private com.sun.star.text.XTextDocument m_xDocument; |
| /// because we implement an import AND an export filter, we must know which one is required |
| private boolean m_bImport; |
| // we need a flag to cancel any running filter operation |
| private int m_nFilterProcState; |
| |
| //______________________________ |
| // native interface |
| /** |
| * special debug helper to get an idea how expensive |
| * the implemented filter operations are realy. |
| * May be usefully for own purposes. |
| * |
| * To see the output inside an office environment |
| * use "soffice ...params... >output.txt" |
| */ |
| private long m_nStart; |
| private long m_nLast ; |
| |
| private void measure( String sText ) |
| { |
| long nNow = System.currentTimeMillis(); |
| System.err.println(sText+"\t"+(nNow-m_nStart)+"\t"+(nNow-m_nLast)); |
| m_nLast = nNow; |
| } |
| |
| //______________________________ |
| // native interface |
| /** |
| * The constructor to initialize every instance |
| * |
| * @param xCompContext |
| * the component context of the office |
| */ |
| //ctor |
| public _AsciiReplaceFilter(XComponentContext Context ) |
| { |
| measure("ctor started"); |
| try |
| { |
| m_Ctx = Context ; |
| m_xMCF = m_Ctx.getServiceManager() ; |
| } |
| catch( Exception e ) |
| { |
| e.printStackTrace(); |
| } |
| |
| // these are safe, thus no errorhandling needed: |
| m_sInternalName = new String() ; |
| m_xDocument = null ; |
| m_bImport = true ; |
| m_nFilterProcState = FILTERPROC_STOPPED ; |
| |
| m_nLast = System.currentTimeMillis(); |
| m_nStart = m_nLast; |
| } |
| |
| //______________________________ |
| // interface XInitialization |
| /** |
| * used for initializing after creation |
| * If an instance of this service is created by UNO we will be called |
| * automaticly after that to get optional parameters of this creation call. |
| * E.g.: The service com.sun.star.document.FilterFactory use such mechanism |
| * to pass our own configuration data to this instance. |
| * |
| * @param lArguments |
| * This array of arbitrary objects represent our own filter configuration |
| * and may optional given parameters of the createWithArguments() call. |
| * |
| * @throws Exception |
| * Every exception will not be handled, but will be |
| * passed to the caller. |
| */ |
| public void initialize( Object[] lArguments ) throws com.sun.star.uno.Exception |
| { |
| measure("initialize {"); |
| |
| if (lArguments.length<1) |
| return; |
| |
| // lArguments[0] = own configuration data |
| com.sun.star.beans.PropertyValue[] lConfig = (com.sun.star.beans.PropertyValue[])lArguments[0]; |
| |
| /* |
| // lArguments[1..n] = optional arguments of create request |
| for (int n=1; n<lArguments.length; ++n) |
| { |
| } |
| */ |
| |
| // analyze own configuration data for our own internal filter name! |
| // Important for generic filter services, which are registered more then once. |
| // They can use this information to find out, which specialization of it |
| // is required. |
| for (int i=0; i<lConfig.length; ++i) |
| { |
| if (lConfig[i].Name.equals("Name")) |
| { |
| synchronized(this) |
| { |
| try |
| { |
| m_sInternalName = AnyConverter.toString(lConfig[i].Value); |
| } |
| catch(com.sun.star.lang.IllegalArgumentException exConvert) {} |
| } |
| } |
| } |
| |
| measure("} initialize"); |
| } |
| |
| //______________________________ |
| // interface XNamed |
| /** |
| * For external user of us we must provide our internal filter name |
| * (which is registered inside configuration package TypeDetection). |
| * User will be able then to ask there for furthe information about us. |
| * Otherwhise we must implement a full featured XPropertySet ... |
| * |
| * @return our internal filter name of configuration |
| */ |
| public String getName() |
| { |
| synchronized(this) |
| { |
| return m_sInternalName; |
| } |
| } |
| |
| /** |
| * It's not allowed for us - neither very easy to change our internal |
| * name during runtime of an office. Because every filter name must |
| * be unambigous ... |
| * So we doesn't implement this method here. |
| */ |
| public void setName( String sName ) |
| { |
| } |
| |
| //______________________________ |
| // interface XImporter |
| /** |
| * This interface is used to tell us: "you will be used for importing a document". |
| * We must save the given model reference to use it inside our own filter request. |
| * |
| * @param xDocument |
| * the document model for importing |
| * |
| * @throw IllegalArgumentException |
| * if given document isn't the right one or seams to be corrupt |
| */ |
| public void setTargetDocument( com.sun.star.lang.XComponent xDocument ) throws com.sun.star.lang.IllegalArgumentException |
| { |
| measure("setTargetDocument {"); |
| |
| if (xDocument==null) |
| throw new com.sun.star.lang.IllegalArgumentException("null reference detected"); |
| |
| com.sun.star.lang.XServiceInfo xInfo = (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface( |
| com.sun.star.lang.XServiceInfo.class, xDocument); |
| if ( ! xInfo.supportsService("com.sun.star.text.TextDocument") ) |
| throw new com.sun.star.lang.IllegalArgumentException( "wrong document type" ); |
| |
| // safe it as target document for import |
| // Don't forget to mark this filter used for importing too |
| synchronized(this) |
| { |
| m_xDocument = (com.sun.star.text.XTextDocument)UnoRuntime.queryInterface( |
| com.sun.star.text.XTextDocument.class, xDocument); |
| m_bImport = true; |
| } |
| |
| measure("} setTargetDocument"); |
| } |
| |
| //______________________________ |
| // interface XExporter |
| /** |
| * This interface is used to tell us: "you will be used for exporting a document". |
| * We must save the given model reference to use it inside our own filter request. |
| * |
| * @param xDocument |
| * the document model for exporting |
| * |
| * @throw IllegalArgumentException |
| * if given document isn't the right one or seams to be corrupt |
| */ |
| public void setSourceDocument( com.sun.star.lang.XComponent xDocument ) throws com.sun.star.lang.IllegalArgumentException |
| { |
| measure("setSourceDocument {"); |
| |
| if (xDocument==null) |
| throw new com.sun.star.lang.IllegalArgumentException( "null reference given" ); |
| |
| com.sun.star.lang.XServiceInfo xInfo = (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface( |
| com.sun.star.lang.XServiceInfo.class, xDocument); |
| if ( ! xInfo.supportsService("com.sun.star.text.TextDocument") ) |
| throw new com.sun.star.lang.IllegalArgumentException( "wrong document type" ); |
| |
| // safe it as source document for export |
| // Don't forget to mark this filter used for exporting too |
| synchronized(this) |
| { |
| m_xDocument = (com.sun.star.text.XTextDocument)UnoRuntime.queryInterface( |
| com.sun.star.text.XTextDocument.class, xDocument); |
| m_bImport = false; |
| } |
| |
| measure("} setSourceDocument"); |
| } |
| |
| |
| //______________________________ |
| // interface XFilter |
| /** |
| * Implements the real filter method. We detect if it must be an import or an export. |
| * Depends on that we use an existing stream (given inside the MediaDescriptor) |
| * or open it by using an URL (must be a part of the descriptor too). |
| * |
| * @param lDescriptor |
| * the MediaDescriptor which describes the document |
| * |
| * @return a bool value which describes if method was successfully or not. |
| */ |
| |
| public boolean filter( com.sun.star.beans.PropertyValue[] lDescriptor ) |
| { |
| measure("filter {"); |
| |
| // first get state of filter operation (import/export) |
| // and try to create or get corresponding streams |
| // Means: analyze given MediaDescriptor |
| // By the way: use synchronized section to get some copies of other |
| // internal states too. |
| FilterOptions aOptions = null ; |
| boolean bImport = false; |
| com.sun.star.text.XTextDocument xText = null ; |
| synchronized(this) |
| { |
| aOptions = new FilterOptions(m_xMCF, m_Ctx, m_bImport, lDescriptor); |
| bImport = m_bImport; |
| xText = m_xDocument; |
| } |
| |
| measure("options analyzed"); |
| |
| if (aOptions.isValid()==false) |
| return false; |
| |
| // start real filtering |
| boolean bState = false; |
| if (bImport) |
| bState = implts_import( xText, aOptions ); |
| else |
| bState = implts_export( xText, aOptions ); |
| |
| measure("} filter"); |
| |
| return bState; |
| } |
| |
| /** |
| * Makes the filter process breakable. To do so the outside code may use threads. |
| * We use a internal "condition" variable wich is queried by the real filter method on |
| * every loop they do. So it's more a polling mechanism. |
| */ |
| public void cancel() |
| { |
| measure("cancel {"); |
| |
| synchronized(this) |
| { |
| if (m_nFilterProcState==FILTERPROC_RUNS) |
| m_nFilterProcState=FILTERPROC_BREAK; |
| } |
| |
| while (true) |
| { |
| synchronized(this) |
| { |
| if (m_nFilterProcState==FILTERPROC_STOPPED) |
| break; |
| } |
| } |
| |
| measure("} cancel"); |
| } |
| |
| //______________________________ |
| // private helper |
| /** |
| * This helper function imports a simple ascii text file into |
| * a text model. We copy every letter to the document. |
| * But if some optional filter options are given |
| * we make some changes: replace chars or complete strings. |
| * |
| * Note: It's not alloed for a filter to seek inside the stream. |
| * Because the outside frameloader has to set the stream position |
| * right and a filter must read till EOF occures only. |
| * |
| * @param xTarget |
| * the target text model to put the data in |
| * |
| * @param aOptions |
| * capsulate all other neccessary informations for this filter request |
| * (streams, replace values ...) |
| * |
| * @return a bool value which describes if method was successfully or not. |
| */ |
| private boolean implts_import( com.sun.star.text.XTextDocument xTarget , |
| FilterOptions aOptions ) |
| { |
| measure("implts_import {"); |
| |
| com.sun.star.text.XSimpleText xText = (com.sun.star.text.XSimpleText)UnoRuntime.queryInterface( |
| com.sun.star.text.XSimpleText.class, |
| xTarget.getText()); |
| |
| measure("cast XSimpleText"); |
| |
| boolean bBreaked = false; |
| |
| try |
| { |
| StringBuffer sBuffer = new StringBuffer(100000); |
| byte[][] lData = new byte[1][]; |
| int nRead = aOptions.m_xInput.readBytes( lData, 4096 ); |
| |
| measure("read first bytes"); |
| |
| while (nRead>0 && !bBreaked) |
| { |
| // copy data from stream to temp. buffer |
| sBuffer.append( new String(lData[0]) ); |
| measure("buffer append ["+nRead+"]"); |
| |
| nRead = aOptions.m_xInput.readBytes( lData, 2048 ); |
| measure("read next bytes"); |
| |
| // check for cancelled filter proc on every loop! |
| synchronized(this) |
| { |
| if (m_nFilterProcState==FILTERPROC_BREAK) |
| { |
| m_nFilterProcState = FILTERPROC_STOPPED; |
| return false; |
| } |
| } |
| measure("break check"); |
| } |
| |
| // Make some replacements inside the buffer. |
| String sText = implts_replace( sBuffer, aOptions ); |
| measure("replace"); |
| |
| // copy current buffer to the document model. |
| // Create a new paragraph for every line inside original file. |
| // May not all data could be readed - but that doesn't matter here. |
| // Reason: somewhere cancelled this function. |
| // But check for optioanl replace request before ... |
| int nStart = 0; |
| int nEnd = -1; |
| int nLength = sText.length(); |
| |
| com.sun.star.text.XTextRange xCursor = (com.sun.star.text.XTextRange)UnoRuntime.queryInterface( |
| com.sun.star.text.XTextRange.class, |
| xText.createTextCursor()); |
| |
| while (true) |
| { |
| nEnd = sText.indexOf('\n',nStart); |
| |
| if (nEnd==-1 && nStart<nLength) |
| nEnd = nLength; |
| |
| if (nEnd==-1) |
| break; |
| |
| String sLine = sText.substring(nStart,nEnd); |
| nStart = nEnd+1; |
| |
| xText.insertString(xCursor,sLine,false); |
| xText.insertControlCharacter(xCursor,com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK,false); |
| |
| // check for cancelled filter proc on every loop! |
| synchronized(this) |
| { |
| if (m_nFilterProcState==FILTERPROC_BREAK) |
| { |
| m_nFilterProcState = FILTERPROC_STOPPED; |
| return false; |
| } |
| } |
| measure("break check"); |
| } |
| |
| measure("set on model"); |
| |
| // with refreshing the document we are on the safe-side, otherwise the first time the filter is used the document is not fully shown (flaw!). |
| com.sun.star.util.XRefreshable xRefresh = (com.sun.star.util.XRefreshable)UnoRuntime.queryInterface( |
| com.sun.star.util.XRefreshable.class, |
| xTarget); |
| xRefresh.refresh(); |
| |
| // If we created used stream - we must close it too. |
| if (aOptions.m_bStreamOwner==true) |
| { |
| aOptions.m_xInput.closeInput(); |
| measure("stream close"); |
| } |
| } |
| catch(com.sun.star.lang.IllegalArgumentException exArgument ) { bBreaked = true; } |
| catch(com.sun.star.io.BufferSizeExceededException exExceed ) { bBreaked = true; } |
| catch(com.sun.star.io.NotConnectedException exConnect ) { bBreaked = true; } |
| catch(com.sun.star.io.IOException exIO ) { bBreaked = true; } |
| |
| |
| |
| measure("} implts_import"); |
| |
| return !bBreaked; |
| } |
| |
| /** |
| * This helper function exports a simple ansi text file from |
| * a text model. We copy every letter from the document. |
| * There are no checks. |
| * |
| * Note: It's not alloed for a filter to seek inside the stream. |
| * Because the outside frameloader has to set the stream position |
| * right and a filter must read till EOF occures only. |
| * |
| * @param xSource |
| * the source text model to get the data from |
| * |
| * @param aOptions |
| * capsulate all other neccessary informations for this filter request |
| * (streams, replace values ...) |
| * |
| * @return a bool value which describes if method was successfully or not. |
| */ |
| private boolean implts_export( com.sun.star.text.XTextDocument xSource , |
| FilterOptions aOptions) |
| { |
| measure("implts_export {"); |
| |
| com.sun.star.text.XTextRange xText = (com.sun.star.text.XSimpleText)UnoRuntime.queryInterface( |
| com.sun.star.text.XSimpleText.class, |
| xSource.getText()); |
| |
| measure("cast XTextRange"); |
| |
| boolean bBreaked = false; |
| |
| try |
| { |
| StringBuffer sBuffer = new StringBuffer(xText.getString()); |
| String sText = implts_replace(sBuffer,aOptions); |
| |
| measure("get text from model"); |
| |
| // Normaly this function isn't realy cancelable |
| // But we following operation can be very expensive. So |
| // this place is the last one to stop it. |
| synchronized(this) |
| { |
| if (m_nFilterProcState==FILTERPROC_BREAK) |
| { |
| m_nFilterProcState = FILTERPROC_STOPPED; |
| return false; |
| } |
| } |
| |
| aOptions.m_xOutput.writeBytes(sText.getBytes()); |
| aOptions.m_xOutput.flush(); |
| |
| measure("written to file"); |
| |
| // If we created used stream - we must close it too. |
| if (aOptions.m_bStreamOwner==true) |
| { |
| aOptions.m_xOutput.closeOutput(); |
| measure("stream close"); |
| } |
| } |
| catch(com.sun.star.io.BufferSizeExceededException exExceed ) { bBreaked = true; } |
| catch(com.sun.star.io.NotConnectedException exConnect ) { bBreaked = true; } |
| catch(com.sun.star.io.IOException exIO ) { bBreaked = true; } |
| |
| measure("} implts_export"); |
| |
| return !bBreaked; |
| } |
| |
| /** |
| * helper function to convert the used StringBuffer into a Strig value. |
| * And we use this chance to have a look on optional filter options |
| * which can invite replacing of strings. |
| */ |
| private String implts_replace( StringBuffer rBuffer, FilterOptions aOptions ) |
| { |
| // replace complete strings first |
| // Because its easiear on a buffer then on a string |
| if ( ! aOptions.m_sOld.equals(aOptions.m_sNew) ) |
| { |
| int nStart = rBuffer.indexOf(aOptions.m_sOld); |
| int nLength = aOptions.m_sNew.length(); |
| int nEnd = nStart+nLength; |
| while (nStart!=-1) |
| { |
| rBuffer.replace(nStart,nEnd,aOptions.m_sNew); |
| nStart = rBuffer.indexOf(aOptions.m_sOld,nEnd); |
| nEnd = nStart+nLength; |
| } |
| } |
| |
| // convert buffer into return format [string] |
| // and convert to lower or upper case if required. |
| String sResult = rBuffer.toString(); |
| if (aOptions.m_bCaseChange==true) |
| { |
| if (aOptions.m_bLower==true) |
| sResult = sResult.toLowerCase(); |
| else |
| sResult = sResult.toUpperCase(); |
| } |
| |
| return sResult; |
| } |
| |
| |
| //______________________________ |
| // interface XServiceInfo |
| /** |
| * This method returns an array of all supported service names. |
| * |
| * @return Array of supported service names. |
| */ |
| public String[] getSupportedServiceNames() |
| { |
| return m_serviceNames; |
| } |
| |
| /** |
| * This method returns true, if the given service will be |
| * supported by this component. |
| * |
| * @param sService |
| * the requested service name |
| * |
| * @return True, if the given service name will be supported; |
| * False otherwhise. |
| */ |
| public boolean supportsService( String sService ) |
| { |
| return ( |
| sService.equals( m_serviceNames[0] ) || |
| sService.equals( m_serviceNames[1] ) || |
| sService.equals( m_serviceNames[2] ) |
| ); |
| } |
| |
| /** |
| * Return the real class name of the component |
| * |
| * @return Class name of the component. |
| */ |
| public String getImplementationName() |
| { |
| return _AsciiReplaceFilter.class.getName(); |
| } |
| |
| |
| } |
| // end of inner class, the wrapper class just has the two methods required for registering the component |
| |
| /** |
| * Gives a factory for creating the service. |
| * This method is called by the <code>JavaLoader</code> |
| * |
| * @param sImplName |
| * The implementation name of the component. |
| * |
| * @return Returns a <code>XSingleComponentFactory</code> for |
| * creating the component. |
| * |
| * @see com.sun.star.comp.loader.JavaLoader |
| */ |
| |
| public static XSingleComponentFactory __getComponentFactory(String sImplName) |
| { |
| XSingleComponentFactory xFactory = null; |
| |
| if ( sImplName.equals( _AsciiReplaceFilter.class.getName() ) ) |
| xFactory = com.sun.star.lib.uno.helper.Factory.createComponentFactory(_AsciiReplaceFilter.class, |
| _AsciiReplaceFilter.m_serviceNames); |
| return xFactory; |
| } |
| |
| |
| /** |
| * Writes the service information into the given registry key. |
| * This method is called by the <code>JavaLoader</code>. |
| * |
| * @param xRegistryKey |
| * Makes structural information (except regarding tree |
| * structures) of a single registry key accessible. |
| * |
| * @return returns true if the operation succeeded |
| * |
| * @see com.sun.star.comp.loader.JavaLoader |
| */ |
| // This method not longer necessary since OOo 3.4 where the component registration |
| // was changed to passive component registration. For more details see |
| // http://wiki.services.openoffice.org/wiki/Passive_Component_Registration |
| |
| // public static boolean __writeRegistryServiceInfo( com.sun.star.registry.XRegistryKey xRegistryKey ) |
| // { |
| // return Factory.writeRegistryServiceInfo( |
| // _AsciiReplaceFilter.class.getName(), |
| // _AsciiReplaceFilter.m_serviceNames, |
| // xRegistryKey ); |
| // } |
| } |