ctakes-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From seanfi...@apache.org
Subject svn commit: r1627110 - in /ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2: ae/ dictionary/ util/
Date Tue, 23 Sep 2014 18:27:06 GMT
Author: seanfinan
Date: Tue Sep 23 18:27:05 2014
New Revision: 1627110

URL: http://svn.apache.org/r1627110
Log:
Extracted interface DictionarySpec and moved implementation to DefaultDictionarySpec
Added UmlsJdbcRareWordDictionary
Changed cTakesHsql.xml to point to UmlsJdbcRareWordDictionary
Updated AbstractJCasTermAnnotator for external updates (index, etc)

Added:
    ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/UmlsJdbcRareWordDictionary.java
    ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DefaultDictionarySpec.java
      - copied, changed from r1625608, ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DictionarySpec.java
Removed:
    ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DictionarySpec.java
Modified:
    ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/ae/AbstractJCasTermAnnotator.java
    ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/DictionaryDescriptorParser.java

Modified: ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/ae/AbstractJCasTermAnnotator.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/ae/AbstractJCasTermAnnotator.java?rev=1627110&r1=1627109&r2=1627110&view=diff
==============================================================================
--- ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/ae/AbstractJCasTermAnnotator.java
(original)
+++ ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/ae/AbstractJCasTermAnnotator.java
Tue Sep 23 18:27:05 2014
@@ -132,8 +132,8 @@ abstract public class AbstractJCasTermAn
    public void process( final JCas jcas ) throws AnalysisEngineProcessException {
       _logger.debug( "Starting processing" );
       final JFSIndexRepository indexes = jcas.getJFSIndexRepository();
-      final AnnotationIndex annotationIndex = indexes.getAnnotationIndex( _lookupWindowType
);
-      if ( annotationIndex == null ) {  // I don't trust AnnotationIndex.size(), so don't
check
+      final AnnotationIndex<Annotation> lookupWindows = indexes.getAnnotationIndex(
_lookupWindowType );
+      if ( lookupWindows == null ) {  // I don't trust AnnotationIndex.size(), so don't check
          return;
       }
       final Map<RareWordDictionary, CollectionMap<TextSpan, Long>> dictionaryTermsMap
@@ -142,12 +142,10 @@ abstract public class AbstractJCasTermAn
          final CollectionMap<TextSpan, Long> textSpanCuis = new HashSetMap<>();
          dictionaryTermsMap.put( dictionary, textSpanCuis );
       }
-      final Iterator windowIterator = annotationIndex.iterator();
       try {
-         while ( windowIterator.hasNext() ) {
-            final Annotation window = (Annotation)windowIterator.next();
-            if ( isWindowOk( window ) ) {
-               processWindow( jcas, window, dictionaryTermsMap );
+         for ( Object window : lookupWindows ) {
+            if ( isWindowOk( (Annotation)window ) ) {
+               processWindow( jcas, (Annotation)window, dictionaryTermsMap );
             }
          }
       } catch ( ArrayIndexOutOfBoundsException iobE ) {
@@ -155,10 +153,11 @@ abstract public class AbstractJCasTermAn
          _logger.warn( iobE.getMessage() );
       }
       // Let the consumer handle uniqueness and ordering - some may not care
+      final Collection<Long> allDictionaryCuis = new HashSet<>();
       for ( Map.Entry<RareWordDictionary, CollectionMap<TextSpan, Long>> dictionaryCuis
: dictionaryTermsMap.entrySet() ) {
+         allDictionaryCuis.clear();
          final RareWordDictionary dictionary = dictionaryCuis.getKey();
          final CollectionMap<TextSpan, Long> textSpanCuis = dictionaryCuis.getValue();
-         final Collection<Long> allDictionaryCuis = new HashSet<>();
          for ( Collection<Long> cuiCodes : textSpanCuis.getAllCollections() ) {
             allDictionaryCuis.addAll( cuiCodes );
          }

Modified: ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/DictionaryDescriptorParser.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/DictionaryDescriptorParser.java?rev=1627110&r1=1627109&r2=1627110&view=diff
==============================================================================
--- ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/DictionaryDescriptorParser.java
(original)
+++ ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/DictionaryDescriptorParser.java
Tue Sep 23 18:27:05 2014
@@ -20,6 +20,7 @@ package org.apache.ctakes.dictionary.loo
 
 import org.apache.ctakes.dictionary.lookup2.concept.ConceptFactory;
 import org.apache.ctakes.dictionary.lookup2.consumer.TermConsumer;
+import org.apache.ctakes.dictionary.lookup2.util.DefaultDictionarySpec;
 import org.apache.ctakes.dictionary.lookup2.util.DictionarySpec;
 import org.apache.log4j.Logger;
 import org.apache.uima.UimaContext;
@@ -152,7 +153,7 @@ final public class DictionaryDescriptorP
     * @param descriptorFile XML-formatted file, see the dictionary-lookup resources file
{@code RareWordTermsUMLS.xml}
     *                       for an example
     * @param uimaContext    -
-    * @return {@link org.apache.ctakes.dictionary.lookup2.util.DictionarySpec} with specification
of dictionaries and a consumer as read from the
+    * @return {@link org.apache.ctakes.dictionary.lookup2.util.DefaultDictionarySpec} with
specification of dictionaries and a consumer as read from the
     * {@code descriptorFile}
     * @throws AnnotatorContextException if the File could not be found/read or the xml could
not be parsed
     */
@@ -164,7 +165,7 @@ final public class DictionaryDescriptorP
       try {
          doc = saxBuilder.build( descriptorFile );
       } catch ( JDOMException | IOException jdomioE ) {
-         throw new AnnotatorContextException( "Could not parse " + descriptorFile.getPath(),
new Object[0], jdomioE );
+         throw new AnnotatorContextException( "Could not parse " + descriptorFile.getPath(),
new Object[ 0 ], jdomioE );
       }
       final Map<String, RareWordDictionary> dictionaries
             = parseDictionaries( uimaContext, doc.getRootElement().getChild( DICTIONARIES_KEY
) );
@@ -175,7 +176,7 @@ final public class DictionaryDescriptorP
       final Map<String, String> pairConceptFactoryNames
             = parsePairingNames( doc.getRootElement().getChild( PAIRS_KEY ), "conceptFactoryName"
);
       final TermConsumer consumer = parseConsumerXml( uimaContext, doc.getRootElement().getChild(
CONSUMER_KEY ) );
-      return new DictionarySpec( pairDictionaryNames, pairConceptFactoryNames, dictionaries,
conceptFactories,
+      return new DefaultDictionarySpec( pairDictionaryNames, pairConceptFactoryNames, dictionaries,
conceptFactories,
             consumer );
    }
 
@@ -225,23 +226,23 @@ final public class DictionaryDescriptorP
       try {
          dictionaryClass = Class.forName( className );
       } catch ( ClassNotFoundException cnfE ) {
-         throw new AnnotatorContextException( "Unknown class " + className, new Object[0],
cnfE );
+         throw new AnnotatorContextException( "Unknown class " + className, new Object[ 0
], cnfE );
       }
       if ( !RareWordDictionary.class.isAssignableFrom( dictionaryClass ) ) {
-         throw new AnnotatorContextException( className + " is not a Rare Word Dictionary",
new Object[0] );
+         throw new AnnotatorContextException( className + " is not a Rare Word Dictionary",
new Object[ 0 ] );
       }
       final Constructor[] constructors = dictionaryClass.getConstructors();
       for ( Constructor constructor : constructors ) {
          try {
             if ( Arrays.equals( constructionArgs, constructor.getParameterTypes() ) ) {
-               final Object[] args = new Object[]{ name, uimaContext, properties };
+               final Object[] args = new Object[] { name, uimaContext, properties };
                return (RareWordDictionary)constructor.newInstance( args );
             }
          } catch ( InstantiationException | IllegalAccessException | InvocationTargetException
iniaitE ) {
-            throw new AnnotatorContextException( "Could not construct " + className, new
Object[0], iniaitE );
+            throw new AnnotatorContextException( "Could not construct " + className, new
Object[ 0 ], iniaitE );
          }
       }
-      throw new AnnotatorContextException( "No Constructor for " + className, new Object[0]
);
+      throw new AnnotatorContextException( "No Constructor for " + className, new Object[
0 ] );
    }
 
 
@@ -290,23 +291,23 @@ final public class DictionaryDescriptorP
       try {
          conceptFactoryClass = Class.forName( className );
       } catch ( ClassNotFoundException cnfE ) {
-         throw new AnnotatorContextException( "Unknown class " + className, new Object[0],
cnfE );
+         throw new AnnotatorContextException( "Unknown class " + className, new Object[ 0
], cnfE );
       }
       if ( !ConceptFactory.class.isAssignableFrom( conceptFactoryClass ) ) {
-         throw new AnnotatorContextException( className + " is not a Concept Factory", new
Object[0] );
+         throw new AnnotatorContextException( className + " is not a Concept Factory", new
Object[ 0 ] );
       }
       final Constructor[] constructors = conceptFactoryClass.getConstructors();
       for ( Constructor constructor : constructors ) {
          try {
             if ( Arrays.equals( constructionArgs, constructor.getParameterTypes() ) ) {
-               final Object[] args = new Object[]{ name, uimaContext, properties };
+               final Object[] args = new Object[] { name, uimaContext, properties };
                return (ConceptFactory)constructor.newInstance( args );
             }
          } catch ( InstantiationException | IllegalAccessException | InvocationTargetException
iniaitE ) {
-            throw new AnnotatorContextException( "Could not construct " + className, new
Object[0], iniaitE );
+            throw new AnnotatorContextException( "Could not construct " + className, new
Object[ 0 ], iniaitE );
          }
       }
-      throw new AnnotatorContextException( "No Constructor for " + className, new Object[0]
);
+      throw new AnnotatorContextException( "No Constructor for " + className, new Object[
0 ] );
    }
 
 
@@ -333,7 +334,7 @@ final public class DictionaryDescriptorP
    static private String getName( final String elementName, final Element element ) throws
AnnotatorContextException {
       final String name = element.getChildText( "name" );
       if ( name == null || name.isEmpty() ) {
-         throw new AnnotatorContextException( "Missing name for " + elementName, new Object[0]
);
+         throw new AnnotatorContextException( "Missing name for " + elementName, new Object[
0 ] );
       }
       return name;
    }
@@ -436,7 +437,7 @@ final public class DictionaryDescriptorP
     */
    private static TermConsumer parseConsumerXml( final UimaContext uimaContext,
                                                  final Element lookupConsumerElement ) throws
-         AnnotatorContextException {
+                                                                                       AnnotatorContextException
{
       Class[] constrArgsConsum = { UimaContext.class, Properties.class, int.class };//ohnlp-Bugs-3296301
       Class[] constrArgsConsumB = { UimaContext.class, Properties.class };
 
@@ -447,28 +448,28 @@ final public class DictionaryDescriptorP
       try {
          consumerClass = Class.forName( consumerClassName );
       } catch ( ClassNotFoundException cnfE ) {
-         throw new AnnotatorContextException( "Unknown class " + consumerClassName, new Object[0],
cnfE );
+         throw new AnnotatorContextException( "Unknown class " + consumerClassName, new Object[
0 ], cnfE );
       }
       if ( !TermConsumer.class.isAssignableFrom( consumerClass ) ) {
          throw new AnnotatorContextException( consumerClassName + " is not a TermConsumer",
-               new Object[0] );
+               new Object[ 0 ] );
       }
       final Constructor[] constructors = consumerClass.getConstructors();
       for ( Constructor constructor : constructors ) {
          try {
             if ( Arrays.equals( constrArgsConsum, constructor.getParameterTypes() ) ) {
-               final Object[] args = new Object[]{ uimaContext, consumerProperties,
-                                                   MAX_LIST_SIZE }; //ohnlp-Bugs-3296301
+               final Object[] args = new Object[] { uimaContext, consumerProperties,
+                                                    MAX_LIST_SIZE }; //ohnlp-Bugs-3296301
                return (TermConsumer)constructor.newInstance( args );
             } else if ( Arrays.equals( constrArgsConsumB, constructor.getParameterTypes()
) ) {
-               final Object[] args = new Object[]{ uimaContext, consumerProperties };
+               final Object[] args = new Object[] { uimaContext, consumerProperties };
                return (TermConsumer)constructor.newInstance( args );
             }
          } catch ( InstantiationException | IllegalAccessException | InvocationTargetException
multE ) {
-            throw new AnnotatorContextException( "Could not construct " + consumerClassName,
new Object[0], multE );
+            throw new AnnotatorContextException( "Could not construct " + consumerClassName,
new Object[ 0 ], multE );
          }
       }
-      throw new AnnotatorContextException( "No Constructor for " + consumerClassName, new
Object[0] );
+      throw new AnnotatorContextException( "No Constructor for " + consumerClassName, new
Object[ 0 ] );
    }
 
    /**

Added: ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/UmlsJdbcRareWordDictionary.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/UmlsJdbcRareWordDictionary.java?rev=1627110&view=auto
==============================================================================
--- ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/UmlsJdbcRareWordDictionary.java
(added)
+++ ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/dictionary/UmlsJdbcRareWordDictionary.java
Tue Sep 23 18:27:05 2014
@@ -0,0 +1,70 @@
+package org.apache.ctakes.dictionary.lookup2.dictionary;
+
+import org.apache.ctakes.dictionary.lookup2.term.RareWordTerm;
+import org.apache.ctakes.dictionary.lookup2.util.FastLookupToken;
+import org.apache.ctakes.dictionary.lookup2.util.UmlsUserApprover;
+import org.apache.log4j.Logger;
+import org.apache.uima.UimaContext;
+
+import java.util.Collection;
+import java.util.Properties;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 9/23/2014
+ */
+final public class UmlsJdbcRareWordDictionary implements RareWordDictionary {
+
+   static private final Logger LOGGER = Logger.getLogger( "UmlsJdbcRareWordDictionary" );
+
+   private final static String URL_PARAM = "umlsUrl";
+   private final static String VENDOR_PARAM = "umlsVendor";
+   private final static String USER_PARAM = "umlsUser";
+   private final static String PASS_PARAM = "umlsPass";
+
+
+   final private RareWordDictionary _delegateDictionary;
+
+
+   public UmlsJdbcRareWordDictionary( final String name, final UimaContext uimaContext, final
Properties properties )
+         throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+      final String umlsUrl = properties.getProperty( URL_PARAM );
+      final String vendor = properties.getProperty( VENDOR_PARAM );
+      final String user = properties.getProperty( USER_PARAM );
+      final String pass = properties.getProperty( PASS_PARAM );
+      final boolean isValidUser = UmlsUserApprover.isValidUMLSUser( umlsUrl, vendor, user,
pass );
+      if ( !isValidUser ) {
+         LOGGER.error( "UMLS Account at " + umlsUrl + " is not valid for user " + user +
" with " + pass );
+         throw new InstantiationException();
+      }
+      _delegateDictionary = new JdbcRareWordDictionary( name, uimaContext, properties );
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getName() {
+      return _delegateDictionary.getName();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<RareWordTerm> getRareWordHits( final FastLookupToken fastLookupToken
) {
+      return _delegateDictionary.getRareWordHits( fastLookupToken );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<RareWordTerm> getRareWordHits( final String rareWordText ) {
+      return _delegateDictionary.getRareWordHits( rareWordText );
+   }
+
+
+}

Copied: ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DefaultDictionarySpec.java
(from r1625608, ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DictionarySpec.java)
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DefaultDictionarySpec.java?p2=ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DefaultDictionarySpec.java&p1=ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DictionarySpec.java&r1=1625608&r2=1627110&rev=1627110&view=diff
==============================================================================
--- ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DictionarySpec.java
(original)
+++ ctakes/trunk/ctakes-dictionary-lookup-fast/src/main/java/org/apache/ctakes/dictionary/lookup2/util/DefaultDictionarySpec.java
Tue Sep 23 18:27:05 2014
@@ -31,12 +31,10 @@ import java.util.HashSet;
 import java.util.Map;
 
 /**
- * Simple Container class that holds a {@link org.apache.ctakes.dictionary.lookup2.dictionary.RareWordDictionary}
- * collection, a a {@link org.apache.ctakes.dictionary.lookup2.concept.ConceptFactory}
- * and a {@link org.apache.ctakes.dictionary.lookup2.consumer.TermConsumer}
+ * {@inheritDoc}
  */
 @Immutable
-final public class DictionarySpec {
+final public class DefaultDictionarySpec implements DictionarySpec {
 
    static private final RareWordDictionary EMPTY_DICTIONARY = new RareWordDictionary() {
       public String getName() {
@@ -66,39 +64,43 @@ final public class DictionarySpec {
       }
    };
 
-   final Collection<String> _pairNames;
-   final Map<String, String> _pairDictionaryNames;
-   final Map<String, String> _pairConceptFactoryNames;
-   final Map<String, RareWordDictionary> _dictionaries;
-   final Map<String, ConceptFactory> _conceptFactories;
-
+   final private Collection<String> _pairNames;
+   final private Map<String, String> _pairDictionaryNames;
+   final private Map<String, String> _pairConceptFactoryNames;
+   final private Map<String, RareWordDictionary> _dictionaries;
+   final private Map<String, ConceptFactory> _conceptFactories;
    final private TermConsumer _termConsumer;
 
    /**
     * @param termConsumer the consumer to add terms to the Cas
     */
-   public DictionarySpec( final Map<String, String> pairDictionaryNames,
-                          final Map<String, String> pairConceptFactoryNames,
-                          final Map<String, RareWordDictionary> dictionaries,
-                          final Map<String, ConceptFactory> conceptFactories,
-                          final TermConsumer termConsumer ) {
+   public DefaultDictionarySpec( final Map<String, String> pairDictionaryNames,
+                                 final Map<String, String> pairConceptFactoryNames,
+                                 final Map<String, RareWordDictionary> dictionaries,
+                                 final Map<String, ConceptFactory> conceptFactories,
+                                 final TermConsumer termConsumer ) {
       _pairNames = new HashSet<>( pairDictionaryNames.keySet() );
       _pairNames.addAll( pairConceptFactoryNames.keySet() );
       // TODO check for completion of pairings
-      _pairDictionaryNames = pairDictionaryNames;
-      _pairConceptFactoryNames = pairConceptFactoryNames;
-      _dictionaries = dictionaries;
-      _conceptFactories = conceptFactories;
+      _pairDictionaryNames = Collections.unmodifiableMap( pairDictionaryNames );
+      _pairConceptFactoryNames = Collections.unmodifiableMap( pairConceptFactoryNames );
+      _dictionaries = Collections.unmodifiableMap( dictionaries );
+      _conceptFactories = Collections.unmodifiableMap( conceptFactories );
       _termConsumer = termConsumer;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public Collection<String> getPairNames() {
       return _pairNames;
    }
 
    /**
-    * @return the dictionary for the given pair name
+    * {@inheritDoc}
     */
+   @Override
    public RareWordDictionary getDictionary( final String pairName ) {
       final String dictionaryName = _pairDictionaryNames.get( pairName );
       if ( dictionaryName != null ) {
@@ -112,8 +114,9 @@ final public class DictionarySpec {
    }
 
    /**
-    * @return the concept factory for concept creation
+    * {@inheritDoc}
     */
+   @Override
    public ConceptFactory getConceptFactory( final String pairName ) {
       final String conceptFactoryName = _pairConceptFactoryNames.get( pairName );
       if ( conceptFactoryName != null ) {
@@ -126,6 +129,10 @@ final public class DictionarySpec {
       return EMPTY_CONCEPT_FACTORY;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public Collection<RareWordDictionary> getPairedDictionaries( final String conceptFactoryName
) {
       final Collection<RareWordDictionary> dictionaries = new HashSet<>();
       for ( Map.Entry<String, String> pairConceptFactoryName : _pairConceptFactoryNames.entrySet()
) {
@@ -136,6 +143,10 @@ final public class DictionarySpec {
       return dictionaries;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public Collection<ConceptFactory> getPairedConceptFactories( final String dictionaryName
) {
       final Collection<ConceptFactory> conceptFactories = new HashSet<>();
       for ( Map.Entry<String, String> pairDictionaryName : _pairDictionaryNames.entrySet()
) {
@@ -146,17 +157,26 @@ final public class DictionarySpec {
       return conceptFactories;
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public Collection<RareWordDictionary> getDictionaries() {
       return new HashSet<>( _dictionaries.values() );
    }
 
+   /**
+    * {@inheritDoc}
+    */
+   @Override
    public Collection<ConceptFactory> getConceptFactories() {
       return new HashSet<>( _conceptFactories.values() );
    }
 
    /**
-    * @return the consumer to add terms to the Cas
+    * {@inheritDoc}
     */
+   @Override
    public TermConsumer getConsumer() {
       return _termConsumer;
    }



Mime
View raw message