1 /*************************************************************************
2  *
3  *  The Contents of this file are made available subject to the terms of
4  *  the BSD license.
5  *
6  *  Copyright 2000, 2010 Oracle and/or its affiliates.
7  *  All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *  1. Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *  3. Neither the name of Sun Microsystems, Inc. nor the names of its
18  *     contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
31  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *************************************************************************/
34 
35 // uno
36 import com.sun.star.lib.uno.helper.ComponentBase;
37 import com.sun.star.uno.UnoRuntime;
38 
39 // factories
40 import com.sun.star.lang.XMultiServiceFactory;
41 import com.sun.star.lang.XSingleServiceFactory;
42 
43 // supported Interfaces
44 import com.sun.star.linguistic2.XSpellChecker;
45 import com.sun.star.linguistic2.XLinguServiceEventBroadcaster;
46 import com.sun.star.lang.XInitialization;
47 import com.sun.star.lang.XComponent;
48 import com.sun.star.lang.XServiceInfo;
49 import com.sun.star.lang.XServiceDisplayName;
50 
51 // Exceptions
52 import com.sun.star.uno.Exception;
53 import com.sun.star.uno.RuntimeException;
54 import com.sun.star.lang.IllegalArgumentException;
55 
56 //used Interfaces
57 import com.sun.star.linguistic2.XLinguServiceEventListener;
58 import com.sun.star.linguistic2.XSpellAlternatives;
59 import com.sun.star.linguistic2.SpellFailure;
60 import com.sun.star.lang.Locale;
61 import com.sun.star.lang.XEventListener;
62 import com.sun.star.lang.EventObject;
63 import com.sun.star.beans.XPropertySet;
64 import com.sun.star.beans.PropertyValue;
65 import com.sun.star.uno.AnyConverter;
66 import com.sun.star.lang.XTypeProvider;
67 import com.sun.star.uno.XInterface;
68 import com.sun.star.uno.Type;
69 
70 import java.util.ArrayList;
71 
72 public class SampleSpellChecker extends ComponentBase implements
73         XSpellChecker,
74         XLinguServiceEventBroadcaster,
75         XInitialization,
76         XServiceDisplayName,
77         XServiceInfo
78 {
79     PropChgHelper_Spell         aPropChgHelper;
80     ArrayList                   aEvtListeners;
81     boolean                     bDisposing;
82 
83     public SampleSpellChecker()
84     {
85         // names of relevant properties to be used
86         String[] aProps = new String[]
87             {
88                 "IsIgnoreControlCharacters",
89                 "IsUseDictionaryList",
90                 "IsGermanPreReform",
91                 "IsSpellUpperCase",
92                 "IsSpellWithDigits",
93                 "IsSpellCapitalization"
94             };
95         aPropChgHelper  = new PropChgHelper_Spell( (XSpellChecker) this, aProps );
96         aEvtListeners   = new ArrayList();
97         bDisposing      = false;
98     }
99 
100     private boolean IsEqual( Locale aLoc1, Locale aLoc2 )
101     {
102         return aLoc1.Language.equals( aLoc2.Language ) &&
103                aLoc1.Country .equals( aLoc2.Country )  &&
104                aLoc1.Variant .equals( aLoc2.Variant );
105     }
106 
107     private boolean GetValueToUse(
108             String          aPropName,
109             boolean         bDefaultVal,
110             PropertyValue[] aProps )
111     {
112         boolean bRes = bDefaultVal;
113 
114         try
115         {
116             // use temporary value if supplied
117             for (int i = 0;  i < aProps.length;  ++i)
118             {
119                 if (aPropName.equals( aProps[i].Name ))
120                 {
121                     Object aObj = aProps[i].Value;
122                     if (AnyConverter.isBoolean( aObj ))
123                     {
124                         bRes = AnyConverter.toBoolean( aObj );
125                         return bRes;
126                     }
127                 }
128             }
129 
130             // otherwise use value from property set (if available)
131             XPropertySet xPropSet = aPropChgHelper.GetPropSet();
132             if (xPropSet != null)   // should always be the case
133             {
134                 Object aObj = xPropSet.getPropertyValue( aPropName );
135                 if (AnyConverter.isBoolean( aObj ))
136                     bRes = AnyConverter.toBoolean( aObj );
137             }
138         }
139         catch (Exception e) {
140             bRes = bDefaultVal;
141         }
142 
143         return bRes;
144     }
145 
146     private boolean IsUpper( String aWord, Locale aLocale )
147     {
148         java.util.Locale aLang = new java.util.Locale(
149                 aLocale.Language, aLocale.Country, aLocale.Variant );
150         return aWord.equals( aWord.toUpperCase( aLang ) );
151     }
152 
153     private boolean HasDigits( String aWord )
154     {
155         int nLen = aWord.length();
156         for (int i = 0;  i < nLen;  ++i)
157         {
158             if (Character.isDigit( aWord.charAt(i) ))
159                 return true;
160         }
161         return false;
162     }
163 
164     private short GetSpellFailure(
165             String aWord,
166             Locale aLocale,
167             PropertyValue[] aProperties )
168     {
169         short nRes = -1;
170 
171         //!! This code needs to be replaced by code calling the actual
172         //!! implementation of your spellchecker
173         boolean bIsGermanPreReform      = GetValueToUse( "IsGermanPreReform", false, aProperties );
174         if (IsEqual( aLocale, new Locale( "de", "DE", "" ) ))
175         {
176             if (bIsGermanPreReform && aWord.equals( "Schifffahrt" ))
177                 nRes = SpellFailure.SPELLING_ERROR;
178             else if (!bIsGermanPreReform && aWord.equals( "Schiffahrt" ))
179                 nRes = SpellFailure.SPELLING_ERROR;
180         }
181         else if (IsEqual( aLocale, new Locale( "en", "US", "" ) ))
182         {
183             // words with 'u', 'U' and 'arizona' are defined to be incorrect
184             boolean bIsValid = !(aWord.indexOf( "u" ) != -1 || aWord.indexOf( "U" ) != -1)
185                                  && !aWord.equals( "arizona" );
186 
187             if (!bIsValid)
188             {
189                 // default value (no other SpellFailure type is applicable)
190                 nRes = SpellFailure.SPELLING_ERROR;
191 
192                 if (aWord.equals( "arizona" ))
193                     nRes = SpellFailure.CAPTION_ERROR;
194                 else if (aWord.equals( "house" ))
195                     nRes = SpellFailure.SPELLING_ERROR;
196                 else if (aWord.equals( "course" ))
197                     nRes = SpellFailure.IS_NEGATIVE_WORD;
198             }
199         }
200 
201         return nRes;
202     }
203 
204     private XSpellAlternatives GetProposals(
205             String aWord,
206             Locale aLocale,
207             PropertyValue[] aProperties )
208     {
209         short    nType = SpellFailure.SPELLING_ERROR;
210         String[] aProposals = null;
211 
212         // get values of relevant properties that may be used.
213         //! The values for 'IsIgnoreControlCharacters' and 'IsUseDictionaryList'
214         //! are handled by the dispatcher! Thus there is no need to access
215         //! them here.
216         boolean bIsGermanPreReform      = GetValueToUse( "IsGermanPreReform", false, aProperties );
217         boolean bIsSpellWithDigits      = GetValueToUse( "IsSpellWithDigits", false, aProperties );
218         boolean bIsSpellUpperCase       = GetValueToUse( "IsSpellUpperCase", false, aProperties );
219         boolean bIsSpellCapitalization  = GetValueToUse( "IsSpellCapitalization", true, aProperties );
220 
221         //!! This code needs to be replaced by code calling the actual
222         //!! implementation of your spellchecker
223         if (IsEqual( aLocale, new Locale( "de", "DE", "" ) ))
224         {
225             if (bIsGermanPreReform && aWord.equals( "Schifffahrt" ))
226             {
227                 nType = SpellFailure.SPELLING_ERROR;
228                 aProposals = new String[]{ "Schiffahrt" };
229             }
230             else if (!bIsGermanPreReform && aWord.equals( "Schiffahrt" ))
231             {
232                 nType = SpellFailure.SPELLING_ERROR;
233                 aProposals = new String[]{ "Schifffahrt" };
234             }
235         }
236         else if (IsEqual( aLocale, new Locale( "en", "US", "" ) ))
237         {
238             if (aWord.equals( "arizona" ))
239             {
240                 nType = SpellFailure.CAPTION_ERROR;
241                 aProposals = new String[]{ "Arizona" };
242             }
243             else if (aWord.equals( "house" ))
244             {
245                 nType = SpellFailure.SPELLING_ERROR;
246                 aProposals = new String[]{ "horse", "home" };
247             }
248             else if (aWord.equals( "course" ))
249             {
250                 nType = SpellFailure.IS_NEGATIVE_WORD;
251                 aProposals = new String[]{ "line", "plan", "approach" };
252             }
253         }
254 
255         // always return a result if word is incorrect,
256         // proposals may be empty though.
257         return new XSpellAlternatives_impl( aWord, aLocale,
258                                             nType, aProposals );
259     }
260 
261     // __________ interface methods __________
262 
263 
264     //*****************
265     //XSupportedLocales
266     //*****************
267     public Locale[] getLocales()
268         throws com.sun.star.uno.RuntimeException
269     {
270         Locale aLocales[] =
271         {
272             new Locale( "de", "DE", "" ),
273             new Locale( "en", "US", "" )
274         };
275 
276         return aLocales;
277     }
278 
279     public boolean hasLocale( Locale aLocale )
280         throws com.sun.star.uno.RuntimeException
281     {
282         boolean bRes = false;
283         if ( IsEqual( aLocale, new Locale( "de", "DE", "" ) )  ||
284              IsEqual( aLocale, new Locale( "en", "US", "" ) ))
285             bRes = true;
286         return bRes;
287     }
288 
289 
290     //*************
291     //XSpellChecker
292     //*************
293     public boolean isValid(
294             String aWord, Locale aLocale,
295             PropertyValue[] aProperties )
296         throws com.sun.star.uno.RuntimeException,
297                IllegalArgumentException
298     {
299         if (IsEqual( aLocale, new Locale() ) || aWord.length() == 0)
300             return true;
301 
302         // linguistic is currently not allowed to throw exceptions
303         // thus we return null which means 'word cannot be spelled'
304         if (!hasLocale( aLocale ))
305             return true;
306 
307         // get values of relevant properties that may be used.
308         //! The values for 'IsIgnoreControlCharacters' and 'IsUseDictionaryList'
309         //! are handled by the dispatcher! Thus there is no need to access
310         //! them here.
311         boolean bIsGermanPreReform      = GetValueToUse( "IsGermanPreReform", false, aProperties );
312         boolean bIsSpellWithDigits      = GetValueToUse( "IsSpellWithDigits", false, aProperties );
313         boolean bIsSpellUpperCase       = GetValueToUse( "IsSpellUpperCase", false, aProperties );
314         boolean bIsSpellCapitalization  = GetValueToUse( "IsSpellCapitalization", true, aProperties );
315 
316         short nFailure = GetSpellFailure( aWord, aLocale, aProperties );
317         if (nFailure != -1)
318         {
319             // postprocess result for errors that should be ignored
320             if (   (!bIsSpellUpperCase  && IsUpper( aWord, aLocale ))
321                 || (!bIsSpellWithDigits && HasDigits( aWord ))
322                 || (!bIsSpellCapitalization
323                     &&  nFailure == SpellFailure.CAPTION_ERROR)
324             )
325                 nFailure = -1;
326         }
327 
328         return nFailure == -1;
329     }
330 
331 
332     public XSpellAlternatives spell(
333             String aWord, Locale aLocale,
334             PropertyValue[] aProperties )
335         throws com.sun.star.uno.RuntimeException,
336                IllegalArgumentException
337     {
338         if (IsEqual( aLocale, new Locale() ) || aWord.length() == 0)
339             return null;
340 
341         // linguistic is currently not allowed to throw exceptions
342         // thus we return null fwhich means 'word cannot be spelled'
343         if (!hasLocale( aLocale ))
344             return null;
345 
346         XSpellAlternatives xRes = null;
347         if (!isValid( aWord, aLocale, aProperties ))
348         {
349             xRes = GetProposals( aWord, aLocale, aProperties );
350         }
351         return xRes;
352     }
353 
354 
355     //*****************************
356     //XLinguServiceEventBroadcaster
357     //*****************************
358     public boolean addLinguServiceEventListener (
359             XLinguServiceEventListener xLstnr )
360         throws com.sun.star.uno.RuntimeException
361     {
362         boolean bRes = false;
363         if (!bDisposing && xLstnr != null)
364             bRes = aPropChgHelper.addLinguServiceEventListener( xLstnr );
365         return bRes;
366     }
367 
368     public boolean removeLinguServiceEventListener(
369             XLinguServiceEventListener xLstnr )
370         throws com.sun.star.uno.RuntimeException
371     {
372         boolean bRes = false;
373         if (!bDisposing && xLstnr != null)
374             bRes = aPropChgHelper.removeLinguServiceEventListener( xLstnr );
375         return bRes;
376     }
377 
378     //********************
379     // XServiceDisplayName
380     //********************
381     public String getServiceDisplayName( Locale aLocale )
382         throws com.sun.star.uno.RuntimeException
383     {
384         return "Java Samples";
385     }
386 
387     //****************
388     // XInitialization
389     //****************
390     public void initialize( Object[] aArguments )
391         throws com.sun.star.uno.Exception,
392                com.sun.star.uno.RuntimeException
393     {
394         int nLen = aArguments.length;
395         if (2 == nLen)
396         {
397             XPropertySet xPropSet = (XPropertySet)UnoRuntime.queryInterface(
398                                          XPropertySet.class, aArguments[0]);
399             // start listening to property changes
400             aPropChgHelper.AddAsListenerTo( xPropSet );
401         }
402     }
403 
404     //*************
405     // XServiceInfo
406     //*************
407     public boolean supportsService( String aServiceName )
408         throws com.sun.star.uno.RuntimeException
409     {
410         String[] aServices = getSupportedServiceNames_Static();
411         int i, nLength = aServices.length;
412         boolean bResult = false;
413 
414         for( i = 0; !bResult && i < nLength; ++i )
415             bResult = aServiceName.equals( aServices[ i ] );
416 
417         return bResult;
418     }
419 
420     public String getImplementationName()
421         throws com.sun.star.uno.RuntimeException
422     {
423         return _aSvcImplName;
424     }
425 
426     public String[] getSupportedServiceNames()
427         throws com.sun.star.uno.RuntimeException
428     {
429         return getSupportedServiceNames_Static();
430     }
431 
432     // __________ static things __________
433 
434     public static String _aSvcImplName = SampleSpellChecker.class.getName();
435 
436     public static String[] getSupportedServiceNames_Static()
437     {
438         String[] aResult = { "com.sun.star.linguistic2.SpellChecker" };
439         return aResult;
440     }
441 
442 
443     /**
444      * Returns a factory for creating the service.
445      * This method is called by the <code>JavaLoader</code>
446      * <p>
447      * @return  returns a <code>XSingleServiceFactory</code> for creating the component
448      * @param   implName     the name of the implementation for which a service is desired
449      * @param   multiFactory the service manager to be used if needed
450      * @param   regKey       the registryKey
451      * @see                  com.sun.star.comp.loader.JavaLoader
452      */
453     public static XSingleServiceFactory __getServiceFactory(
454         String aImplName,
455         XMultiServiceFactory xMultiFactory,
456         com.sun.star.registry.XRegistryKey xRegKey )
457     {
458         XSingleServiceFactory xSingleServiceFactory = null;
459         if( aImplName.equals( _aSvcImplName ) )
460         {
461             xSingleServiceFactory = new OneInstanceFactory(
462                     SampleSpellChecker.class, _aSvcImplName,
463                     getSupportedServiceNames_Static(),
464                     xMultiFactory );
465         }
466         return xSingleServiceFactory;
467     }
468 
469     /**
470      * Writes the service information into the given registry key.
471      * This method is called by the <code>JavaLoader</code>
472      * <p>
473      * @return  returns true if the operation succeeded
474      * @param   xRegKey       the registryKey
475      * @see                  com.sun.star.comp.loader.JavaLoader
476      */
477     // This method not longer necessary since OOo 3.4 where the component registration
478     // was changed to passive component registration. For more details see
479     // http://wiki.services.openoffice.org/wiki/Passive_Component_Registration
480 
481 //     public static boolean __writeRegistryServiceInfo(
482 //             com.sun.star.registry.XRegistryKey xRegKey )
483 //     {
484 //         boolean bResult = true;
485 //         String[] aServices = getSupportedServiceNames_Static();
486 //         int i, nLength = aServices.length;
487 //         for( i = 0; i < nLength; ++i )
488 //         {
489 //             bResult = bResult && com.sun.star.comp.loader.FactoryHelper.writeRegistryServiceInfo(
490 //                 _aSvcImplName, aServices[i], xRegKey );
491 //         }
492 //         return bResult;
493 //     }
494 }
495 
496