32
32
import java.io.IOException;
33
33
import java.io.UnsupportedEncodingException;
34
34
import java.nio.ByteBuffer;
35
import java.util.ArrayList;
35
36
import java.util.Arrays;
36
37
import java.util.HashSet;
37
38
import java.util.Iterator;
38
39
import java.util.List;
40
import java.util.NoSuchElementException;
39
41
import java.util.Set;
40
42
import java.util.Vector;
41
43
import java.util.regex.Matcher;
42
44
import java.util.regex.Pattern;
43
import java.util.NoSuchElementException;
44
import java.lang.UnsupportedOperationException;
46
46
import android.content.SharedPreferences;
47
47
import android.provider.Contacts;
334
334
private final static int NAMELEVEL_FN = 2;
335
335
private final static int NAMELEVEL_N = 3;
337
private final static int MULTILINE_NONE = 0;
338
private final static int MULTILINE_ENCODED = 1; // v2.1 quoted-printable
339
private final static int MULTILINE_ESCAPED = 2; // v2.1 \\CRLF
340
private final static int MULTILINE_FOLDED = 3; // v3.0 folding
337
342
private String _version = null;
338
343
private Vector< ByteBuffer > _buffers = null;
339
344
private int _name_level = NAMELEVEL_NONE;
340
private boolean _parser_in_encoded_multiline = false;
341
private boolean _parser_in_folded_multiline = false;
345
private int _parser_multiline_state = MULTILINE_NONE;
342
346
private String _parser_current_name_and_params = null;
343
347
private String _parser_buffered_value_so_far = "";
469
473
String name_and_params;
472
if( _parser_in_encoded_multiline ||
473
_parser_in_folded_multiline )
476
if( _parser_multiline_state != MULTILINE_NONE )
475
478
// if we're currently in a multi-line value, use the stored
476
479
// property name and parameters
477
480
name_and_params = _parser_current_name_and_params;
482
// skip some initial line characters, depending on the type
483
// of multi-line we're handling
479
484
pos = buffer.position();
481
// for folded multi-lines, skip the single space at the
482
// start of the next line
483
if( _parser_in_folded_multiline )
485
switch( _parser_multiline_state )
487
case MULTILINE_FOLDED:
486
// else, this must be an encoded multi-line, so skip any
487
// whitespace we find at the start of the next line
490
case MULTILINE_ENCODED:
489
491
while( pos < buffer.limit() && (
490
492
buffer.get( pos ) == ' ' ||
491
493
buffer.get( pos ) == '\t' ) )
502
// take us out of multi-line so that we can re-detect that
503
// this line is a multi-line or not
504
_parser_multiline_state = MULTILINE_NONE;
552
562
// unencoding_result = unencodeBase64( props[ 1 ], charset );
553
563
if( unencoding_result != null ) {
554
564
value = unencoding_result.getBuffer();
555
_parser_in_encoded_multiline =
556
unencoding_result.isAnotherLineRequired();
565
if( unencoding_result.isAnotherLineRequired() )
566
_parser_multiline_state = MULTILINE_ENCODED;
559
569
// convert 8-bit ASCII charset to US-ASCII
571
581
throw new ParseException( R.string.error_vcf_charset );
584
// for some entries that have semicolon-separated value parts,
585
// check to see if the value ends in an escape character, which
586
// indicates that we have a multi-line value
587
if( ( name_param_parts[ 0 ].equals( "N" ) ||
588
name_param_parts[ 0 ].equals( "ORG" ) ||
589
name_param_parts[ 0 ].equals( "ADR" ) ) &&
590
doesStringEndInAnEscapeChar( string_value ) )
592
_parser_multiline_state = MULTILINE_ESCAPED;
593
string_value = string_value.substring( 0,
594
string_value.length() - 1 );
574
597
// now we know whether we're in an encoding multi-line,
575
598
// determine if we're in a v3 folded multi-line or not
576
_parser_in_folded_multiline = !_parser_in_encoded_multiline &&
577
_version.equals( "3.0" ) && next_line_looks_folded;
599
if( _parser_multiline_state == MULTILINE_NONE &&
600
_version.equals( "3.0" ) && next_line_looks_folded )
602
_parser_multiline_state = MULTILINE_FOLDED;
579
// handle multi-line requests
580
if( _parser_in_encoded_multiline ||
581
_parser_in_folded_multiline )
605
// handle multi-lines by buffering them and parsing them when we
606
// are processing the last line in a multi-line sequence
607
if( _parser_multiline_state != MULTILINE_NONE ) {
583
608
_parser_buffered_value_so_far += string_value;
587
// add on buffered multi-line content
588
611
String complete_value =
589
_parser_buffered_value_so_far + string_value;
612
( _parser_buffered_value_so_far + string_value ).trim();
591
614
// ignore empty values
592
615
if( complete_value.length() < 1 ) return;
602
625
parseTEL( name_param_parts, complete_value );
603
626
else if( name_param_parts[ 0 ].equals( "EMAIL" ) )
604
627
parseEMAIL( name_param_parts, complete_value );
628
else if( name_param_parts[ 0 ].equals( "ADR" ) )
629
parseADR( name_param_parts, complete_value );
633
private boolean doesStringEndInAnEscapeChar( String string )
635
// count the number of backslashes at the end of the string
637
for( int a = string.length() - 1; a >= 0; a-- )
638
if( string.charAt( a ) == '\\' )
643
// if there are an even number of backslashes then the final one
645
return ( count & 1 ) == 1;
648
private String[] splitValueBySemicolon( String value )
650
// split string in to parts by semicolon
651
ArrayList< String > parts = new ArrayList< String >(
652
Arrays.asList( value.split( ";" ) ) );
655
for( int a = 0; a < parts.size(); a++ )
657
String str = parts.get( a );
659
// look for parts that end in an escape character, but ignore
660
// the final part. We've already detected escape chars at the
661
// end of the final part in parseLine() and handled multi-lines
663
if( a < parts.size() - 1 &&
664
doesStringEndInAnEscapeChar( str ) )
666
// join the next part to this part and remove the next part
667
parts.set( a, str.substring( 0, str.length() - 1 ) +
668
';' + parts.get( a + 1 ) );
669
parts.remove( a + 1 );
671
// re-visit this part
676
// trim and replace string
681
String[] ret = new String[ parts.size() ];
682
return parts.toArray( ret );
608
685
private void parseN( String[] params, String value )
613
690
if( _name_level >= NAMELEVEL_N ) return;
615
692
// get name parts
616
String[] name_parts = value.split( ";" );
617
for( int i = 0; i < name_parts.length; i++ )
618
name_parts[ i ] = name_parts[ i ].trim();
693
String[] name_parts = splitValueBySemicolon( value );
652
727
if( _name_level >= NAMELEVEL_ORG ) return;
655
String[] org_parts = value.split( ";" );
656
for( int i = 0; i < org_parts.length; i++ )
657
org_parts[ i ] = org_parts[ i ].trim();
730
String[] org_parts = splitValueBySemicolon( value );
660
733
if( org_parts.length > 1 && org_parts[ 0 ].length() == 0 )
661
734
value = org_parts[ 1 ];
735
else if( org_parts.length > 1 && org_parts[ 1 ].length() > 0 )
736
value = org_parts[ 0 ] + ", " + org_parts[ 1 ];
663
738
value = org_parts[ 0 ];
706
781
Set< String > types = extractTypes( params, Arrays.asList(
707
782
"PREF", "WORK", "HOME", "INTERNET" ) );
709
// here's the logic...
710
785
boolean preferred = types.contains( "PREF" );
711
786
if( types.contains( "WORK" ) )
712
787
addEmail( value, Contacts.ContactMethods.TYPE_WORK, preferred );
714
789
addEmail( value, Contacts.ContactMethods.TYPE_HOME, preferred );
792
private void parseADR( String[] params, String value )
793
throws ParseException, SkipContactException
796
String[] adr_parts = splitValueBySemicolon( value );
800
for( int a = 0; a < adr_parts.length; a++ ) {
801
if( value.length() > 0 ) value += "\n";
802
value += adr_parts[ a ].trim();
805
Set< String > types = extractTypes( params, Arrays.asList(
806
"PREF", "WORK", "HOME", "INTERNET" ) );
809
if( types.contains( "WORK" ) )
810
addAddress( value, Contacts.ContactMethods.TYPE_WORK );
812
addAddress( value, Contacts.ContactMethods.TYPE_HOME);
717
815
public void finaliseParsing()
718
816
throws ParseException, SkipContactException,
719
817
AbortImportException