/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts
6 by edam
- added GPL header comments to all files
1
/*
2
 * Importer.java
3
 *
4
 * Copyright (C) 2009 Tim Marston <edam@waxworlds.org>
5
 *
6
 * This file is part of the Import Contacts program (hereafter referred
7
 * to as "this program"). For more information, see
8
 * http://www.waxworlds.org/edam/software/android/import-contacts
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
1 by edam
Initial import
24
package org.waxworlds.importcontacts;
25
26
import java.util.HashMap;
27
import java.util.HashSet;
28
import java.util.Iterator;
29
import java.util.Set;
30
import java.util.regex.Matcher;
31
import java.util.regex.Pattern;
32
33
import android.content.ContentUris;
34
import android.content.ContentValues;
35
import android.content.SharedPreferences;
36
import android.database.Cursor;
37
import android.net.Uri;
38
import android.os.Message;
39
import android.provider.Contacts;
40
41
public class Importer extends Thread
42
{
3 by edam
- added "all done" message
43
	public final static int ACTION_GOBACK = 0;
44
	public final static int ACTION_ABORT = 1;
45
	public final static int ACTION_ALLDONE = 2;
1 by edam
Initial import
46
47
	public final static int RESPONSE_NEGATIVE = 0;
48
	public final static int RESPONSE_POSITIVE = 1;
49
50
	public final static int RESPONSEEXTRA_NONE = 0;
51
	public final static int RESPONSEEXTRA_ALWAYS = 1;
52
53
	private Doit _doit;
54
	private int _response;
55
	private int _responseExtra;
56
	private HashMap< String, Long > _contacts;
57
	private HashMap< Long, HashSet< String > > _contactNumbers;
58
	private HashMap< Long, HashSet< String > > _contactEmails;
59
	private int _mergeSetting;
60
	private int _lastMergeDecision;
61
	private boolean _abort = false;
3 by edam
- added "all done" message
62
	private boolean _isFinished = false;
1 by edam
Initial import
63
64
	public class ContactData
65
	{
66
		class PhoneData
67
		{
68
			public String _number;
69
			public int _type;
70
			public boolean _isPreferred;
71
72
			public PhoneData( String number, int type, boolean isPreferred ) {
73
				_number = number;
74
				_type = type;
75
				_isPreferred = isPreferred;
76
			}
77
78
			public String getNumber() {
79
				return _number;
80
			}
81
82
			public int getType() {
83
				return _type;
84
			}
85
86
			public boolean isPreferred() {
87
				return _isPreferred;
88
			}
89
		}
90
91
		class EmailData
92
		{
93
			private String _email;
94
			public int _type;
95
			private boolean _isPreferred;
96
97
			public EmailData( String email, int type, boolean isPreferred ) {
98
				_email = email;
99
				_type = type;
100
				_isPreferred = isPreferred;
101
			}
102
103
			public String getAddress() {
104
				return _email;
105
			}
106
107
			public int getType() {
108
				return _type;
109
			}
110
111
			public boolean isPreferred() {
112
				return _isPreferred;
113
			}
114
		}
115
116
		public String _name = null;
117
		public HashMap< String, PhoneData > _phones = null;
118
		public HashMap< String, EmailData > _emails = null;
119
120
		protected void setName( String name )
121
		{
122
			_name = name;
123
		}
124
125
		public String getName()
126
		{
127
			return _name;
128
		}
129
130
		protected void addPhone( String number, int type, boolean isPreferred )
131
		{
132
			if( _phones == null ) _phones = new HashMap< String, PhoneData >();
133
			if( !_phones.containsKey( number ) )
134
				_phones.put( number,
135
						new PhoneData( number, type, isPreferred ) );
136
		}
137
138
		protected void addEmail( String email, int type, boolean isPreferred )
139
		{
140
			if( _emails == null ) _emails = new HashMap< String, EmailData >();
141
			if( !_emails.containsKey( email ) )
142
				_emails.put( email, new EmailData( email, type, isPreferred ) );
143
		}
144
	}
145
146
	protected class AbortImportException extends Exception { };
147
148
	public Importer( Doit doit )
149
	{
150
		_doit = doit;
151
152
		SharedPreferences prefs = getSharedPreferences();
9 by edam
- added scroll view to all layouts
153
		_mergeSetting = prefs.getInt( "merge_setting", Doit.ACTION_PROMPT );
1 by edam
Initial import
154
	}
155
156
	@Override
157
	public void run()
158
	{
159
		try
160
		{
161
			// cache current contact names
162
			buildContactsCache();
163
164
			// do the import
165
			onImport();
166
167
			// done!
3 by edam
- added "all done" message
168
			finish( ACTION_ALLDONE );
1 by edam
Initial import
169
		}
170
		catch( AbortImportException e )
171
		{}
3 by edam
- added "all done" message
172
173
		// flag as finished to prevent interrupts
174
		setIsFinished();
175
	}
176
177
	synchronized private void setIsFinished()
178
	{
179
		_isFinished = true;
1 by edam
Initial import
180
	}
181
182
	protected void onImport() throws AbortImportException
183
	{
184
	}
185
186
	public void wake()
187
	{
188
		wake( 0, RESPONSEEXTRA_NONE );
189
	}
190
191
	public void wake( int response )
192
	{
193
		wake( response, RESPONSEEXTRA_NONE );
194
	}
195
196
	synchronized public void wake( int response, int responseExtra )
197
	{
198
		_response = response;
199
		_responseExtra = responseExtra;
200
		notify();
201
	}
202
3 by edam
- added "all done" message
203
	synchronized public boolean setAbort()
1 by edam
Initial import
204
	{
3 by edam
- added "all done" message
205
		if( !_isFinished && !_abort ) {
206
			_abort = true;
207
			notify();
208
			return true;
209
		}
210
		return false;
1 by edam
Initial import
211
	}
212
213
	protected SharedPreferences getSharedPreferences()
214
	{
215
		return _doit.getSharedPreferences();
216
	}
217
218
	protected void showError( int res ) throws AbortImportException
219
	{
220
		showError( _doit.getText( res ).toString() );
221
	}
222
223
	synchronized protected void showError( String message )
224
			throws AbortImportException
225
	{
226
		checkAbort();
227
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
228
				_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
229
		try {
230
			wait();
231
		}
232
		catch( InterruptedException e ) { }
3 by edam
- added "all done" message
233
		// no need to check if an abortion happened during the wait, we are
234
		// about to finish anyway!
235
		finish( ACTION_ABORT );
1 by edam
Initial import
236
	}
237
238
	protected void showFatalError( int res ) throws AbortImportException
239
	{
240
		showFatalError( _doit.getText( res ).toString() );
241
	}
242
243
	synchronized protected void showFatalError( String message )
244
			throws AbortImportException
245
	{
246
		checkAbort();
247
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
248
				_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
249
		try {
250
			wait();
251
		}
252
		catch( InterruptedException e ) { }
3 by edam
- added "all done" message
253
		// no need to check if an abortion happened during the wait, we are
254
		// about to finish anyway!
255
		finish( ACTION_ABORT );
1 by edam
Initial import
256
	}
257
258
	protected boolean showContinue( int res ) throws AbortImportException
259
	{
260
		return showContinue( _doit.getText( res ).toString() );
261
	}
262
263
	synchronized protected boolean showContinue( String message )
264
			throws AbortImportException
265
	{
266
		checkAbort();
267
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
268
				_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) );
1 by edam
Initial import
269
		try {
270
			wait();
271
		}
272
		catch( InterruptedException e ) { }
3 by edam
- added "all done" message
273
274
		// check if an abortion happened during the wait
275
		checkAbort();
276
1 by edam
Initial import
277
		return _response == RESPONSE_POSITIVE;
278
	}
279
280
	protected void setProgressMessage( int res ) throws AbortImportException
281
	{
282
		checkAbort();
283
		_doit._handler.sendMessage( Message.obtain( _doit._handler,
3 by edam
- added "all done" message
284
				Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) );
1 by edam
Initial import
285
	}
286
287
	protected void setProgressMax( int maxProgress )
288
			throws AbortImportException
289
	{
290
		checkAbort();
291
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
292
				_doit._handler, Doit.MESSAGE_SETMAXPROGRESS,
1 by edam
Initial import
293
				new Integer( maxProgress ) ) );
294
	}
295
296
	protected void setTmpProgress( int tmpProgress ) throws AbortImportException
297
	{
298
		checkAbort();
299
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
300
				_doit._handler, Doit.MESSAGE_SETTMPPROGRESS,
1 by edam
Initial import
301
				new Integer( tmpProgress ) ) );
302
	}
303
304
	protected void setProgress( int progress ) throws AbortImportException
305
	{
306
		checkAbort();
307
		_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
308
				_doit._handler, Doit.MESSAGE_SETPROGRESS,
1 by edam
Initial import
309
				new Integer( progress ) ) );
310
	}
311
3 by edam
- added "all done" message
312
	protected void finish( int action ) throws AbortImportException
1 by edam
Initial import
313
	{
3 by edam
- added "all done" message
314
		// update UI to reflect action
315
		int message;
316
		switch( action )
317
		{
318
		case ACTION_GOBACK:		message = Doit.MESSAGE_FINISHED_GOBACK; break;
319
		case ACTION_ALLDONE:	message = Doit.MESSAGE_FINISHED_ALLDONE; break;
320
		default:	// fall through
321
		case ACTION_ABORT:		message = Doit.MESSAGE_FINISHED; break;
322
		}
323
		_doit._handler.sendEmptyMessage( message );
1 by edam
Initial import
324
3 by edam
- added "all done" message
325
		// stop
326
		throw new AbortImportException();
1 by edam
Initial import
327
	}
328
329
	protected CharSequence getText( int res )
330
	{
331
		return _doit.getText( res );
332
	}
333
334
	protected boolean isImportRequired( String name )
335
			throws AbortImportException
336
	{
337
		checkAbort();
338
		return isImportRequired( name, _mergeSetting );
339
	}
340
3 by edam
- added "all done" message
341
	synchronized private boolean isImportRequired( String name,
342
			int mergeSetting ) throws AbortImportException
1 by edam
Initial import
343
	{
344
		_lastMergeDecision = mergeSetting;
345
346
		// handle special cases
347
		switch( mergeSetting )
348
		{
9 by edam
- added scroll view to all layouts
349
		case Doit.ACTION_KEEP:
1 by edam
Initial import
350
			// if we keep contacts on duplicate, we better check for one
351
			return !_contacts.containsKey( name );
352
9 by edam
- added scroll view to all layouts
353
		case Doit.ACTION_PROMPT:
1 by edam
Initial import
354
			// if we are prompting on duplicate, we better check for one
355
			if( !_contacts.containsKey( name ) )
356
				return true;
357
358
			// ok, it exists, so do prompt
359
			_doit._handler.sendMessage( Message.obtain(
3 by edam
- added "all done" message
360
					_doit._handler, Doit.MESSAGE_MERGEPROMPT, name ) );
1 by edam
Initial import
361
			try {
362
				wait();
363
			}
364
			catch( InterruptedException e ) { }
365
3 by edam
- added "all done" message
366
			// check if an abortion happened during the wait
367
			checkAbort();
368
1 by edam
Initial import
369
			// if "always" was selected, make choice permenant
370
			if( _responseExtra == RESPONSEEXTRA_ALWAYS )
371
				_mergeSetting = _response;
372
373
			// recurse, with out new merge setting
374
			return isImportRequired( name, _response );
375
		}
376
377
		// for all other cases (either overwriting or merging) we will need the
378
		// imported data
379
		return true;
380
	}
381
382
	protected void skipContact() throws AbortImportException
383
	{
384
		checkAbort();
3 by edam
- added "all done" message
385
		_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
1 by edam
Initial import
386
	}
387
388
	protected void importContact( ContactData contact )
389
			throws AbortImportException
390
	{
391
		checkAbort();
392
3 by edam
- added "all done" message
393
//		if( !showContinue( "====[ IMPORTING ]====\n: " + contact._name ) )
394
//			finish( ACTION_ABORT );
1 by edam
Initial import
395
396
		ContentValues values = new ContentValues();
397
		boolean uiInformed = false;
398
399
		// does contact exist already?
400
		Uri contactUri = null;
401
		Long id;
402
		if( ( id = (Long)_contacts.get( contact._name ) ) != null )
403
		{
404
			// should we skip this import altogether?
9 by edam
- added scroll view to all layouts
405
			if( _lastMergeDecision == Doit.ACTION_KEEP ) return;
1 by edam
Initial import
406
407
			// get contact's URI
408
			contactUri = ContentUris.withAppendedId(
409
					Contacts.People.CONTENT_URI, id );
410
411
			// should we destroy the existing contact before importing?
9 by edam
- added scroll view to all layouts
412
			if( _lastMergeDecision == Doit.ACTION_OVERWRITE ) {
1 by edam
Initial import
413
				_doit.getContentResolver().delete( contactUri, null, null );
414
				contactUri = null;
415
416
				// upate the UI
3 by edam
- added "all done" message
417
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTOVERWRITTEN );
1 by edam
Initial import
418
				uiInformed = true;
419
420
				// update cache
421
				_contacts.remove( contact._name );
422
			}
423
		}
424
425
		// if we don't have a contact URI it is because the contact never
426
		// existed or because we deleted it
427
		if( contactUri == null )
428
		{
429
			// create a new contact
430
			values.put( Contacts.People.NAME, contact._name );
431
			contactUri = _doit.getContentResolver().insert(
432
					Contacts.People.CONTENT_URI, values );
433
			id = ContentUris.parseId( contactUri );
434
			if( id <= 0 ) return;	// shouldn't happen!
435
436
			// add them to the "My Contacts" group
12 by edam
- bugfix: add contacts to the "my contacts" group didn't actually work on a real device. So we're doing it a different way.
437
			Contacts.People.addToMyContactsGroup(
438
					_doit.getContentResolver(), id );
1 by edam
Initial import
439
440
			// update cache
441
			_contacts.put( contact._name, id );
442
443
			// update UI
444
			if( !uiInformed ) {
3 by edam
- added "all done" message
445
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTCREATED );
1 by edam
Initial import
446
				uiInformed = true;
447
			}
448
		}
449
450
		// update UI
451
		if( !uiInformed )
3 by edam
- added "all done" message
452
			_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTMERGED );
1 by edam
Initial import
453
454
		// import contact parts
455
		if( contact._phones != null )
456
			importContactPhones( contactUri, contact._phones );
457
		if( contact._emails != null )
458
			importContactEmails( contactUri, contact._emails );
459
	}
460
461
	private void importContactPhones( Uri contactUri,
462
			HashMap< String, ContactData.PhoneData > phones )
463
	{
464
		Long contactId = ContentUris.parseId( contactUri );
465
		Uri contactPhonesUri = Uri.withAppendedPath( contactUri,
466
				Contacts.People.Phones.CONTENT_DIRECTORY );
467
		Set phonesKeys = phones.keySet();
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
468
469
		// add phone numbers
1 by edam
Initial import
470
		Iterator i = phonesKeys.iterator();
471
		while( i.hasNext() ) {
472
			ContactData.PhoneData phone = phones.get( i.next() );
473
474
			// we don't want to add this number if it's crap, or it already
475
			// exists (which would cause a duplicate to be created). We don't
476
			// take in to account the type when checking for duplicates. This is
477
			// intentional: types aren't really very reliable. We assume that
478
			// if the number exists at all, it doesn't need importing. Because
479
			// of this, we also can't update the cache (which we don't need to
480
			// anyway, so it's not a problem).
481
			String number = sanitisePhoneNumber( phone._number );
482
			if( number == null ) continue;
483
			HashSet< String > numbers = _contactNumbers.get( contactId );
484
			if( numbers != null && numbers.contains( number ) ) continue;
485
486
			// add phone number
487
			ContentValues values = new ContentValues();
488
			values.put( Contacts.Phones.TYPE, phone._type );
489
			values.put( Contacts.Phones.NUMBER, phone._number );
490
			if( phone._isPreferred ) values.put( Contacts.Phones.ISPRIMARY, 1 );
491
			_doit.getContentResolver().insert( contactPhonesUri, values );
492
		}
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
493
494
		// now add those phone numbers to the cache to prevent the addition of
495
		// duplicate data from another file
496
		i = phonesKeys.iterator();
497
		while( i.hasNext() ) {
498
			ContactData.PhoneData phone = phones.get( i.next() );
499
500
			String number = sanitisePhoneNumber( phone._number );
501
			if( number != null ) {
502
				HashSet< String > numbers = _contactNumbers.get( contactId );
503
				if( numbers == null ) {
504
					_contactNumbers.put( contactId, new HashSet< String >() );
505
					numbers = _contactNumbers.get( contactId );
506
				}
507
				numbers.add( number );
508
			}
509
		}
1 by edam
Initial import
510
	}
511
512
	private void importContactEmails( Uri contactUri,
513
			HashMap< String, ContactData.EmailData > emails )
514
	{
515
		Long contactId = ContentUris.parseId( contactUri );
516
		Uri contactContactMethodsUri = Uri.withAppendedPath( contactUri,
517
				Contacts.People.ContactMethods.CONTENT_DIRECTORY );
518
		Set emailsKeys = emails.keySet();
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
519
520
		// add email addresses
1 by edam
Initial import
521
		Iterator i = emailsKeys.iterator();
522
		while( i.hasNext() ) {
523
			ContactData.EmailData email = emails.get( i.next() );
524
525
			// like with phone numbers, we don't want to add this email address
526
			// if it exists already or we would introduce duplicates.
527
			String address = sanitiseEmailAddress( email.getAddress() );
528
			if( address == null ) continue;
529
			HashSet< String > addresses = _contactEmails.get( contactId );
530
			if( addresses != null && addresses.contains( address ) ) continue;
531
532
			// add phone number
533
			ContentValues values = new ContentValues();
534
			values.put( Contacts.ContactMethods.KIND, Contacts.KIND_EMAIL );
535
			values.put( Contacts.ContactMethods.DATA, email.getAddress() );
536
			values.put( Contacts.ContactMethods.TYPE, email.getType() );
537
			if( email.isPreferred() )
538
				values.put( Contacts.ContactMethods.ISPRIMARY, 1 );
539
			_doit.getContentResolver().insert( contactContactMethodsUri,
540
					values );
541
		}
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
542
543
		// now add those email addresses to the cache to prevent the addition of
544
		// duplicate data from another file
545
		i = emailsKeys.iterator();
546
		while( i.hasNext() ) {
547
			ContactData.EmailData email = emails.get( i.next() );
548
549
			String address = sanitiseEmailAddress( email.getAddress() );
550
			if( address != null ) {
551
				HashSet< String > addresses = _contactEmails.get( contactId );
552
				if( addresses == null ) {
553
					_contactEmails.put( contactId, new HashSet< String >() );
554
					addresses = _contactEmails.get( contactId );
555
				}
556
				addresses.add( address );
557
			}
558
		}
1 by edam
Initial import
559
	}
560
3 by edam
- added "all done" message
561
	synchronized protected void checkAbort() throws AbortImportException
1 by edam
Initial import
562
	{
563
		if( _abort ) {
564
			// stop
565
			throw new AbortImportException();
566
		}
567
	}
568
569
	private void buildContactsCache() throws AbortImportException
570
	{
571
		// update UI
572
		setProgressMessage( R.string.doit_caching );
573
574
		String[] cols;
575
		Cursor cur;
576
577
		// init contacts caches
578
		_contacts = new HashMap< String, Long >();
579
		_contactNumbers = new HashMap< Long, HashSet< String > >();
580
		_contactEmails = new HashMap< Long, HashSet< String > >();
581
582
		// query and store map of contact names to ids
583
		cols = new String[] { Contacts.People._ID, Contacts.People.NAME };
584
		cur = _doit.managedQuery( Contacts.People.CONTENT_URI,
585
				cols, null, null, null);
586
		if( cur.moveToFirst() ) {
587
			int idCol = cur.getColumnIndex( Contacts.People._ID );
588
			int nameCol = cur.getColumnIndex( Contacts.People.NAME );
589
			do {
590
				_contacts.put( cur.getString( nameCol ), cur.getLong( idCol ) );
591
			} while( cur.moveToNext() );
592
		}
593
594
		// query and store map of contact ids to sets of phone numbers
595
		cols = new String[] { Contacts.Phones.PERSON_ID,
596
				Contacts.Phones.NUMBER };
597
		cur = _doit.managedQuery( Contacts.Phones.CONTENT_URI,
598
				cols, null, null, null);
599
		if( cur.moveToFirst() ) {
600
			int personIdCol = cur.getColumnIndex( Contacts.Phones.PERSON_ID );
601
			int numberCol = cur.getColumnIndex( Contacts.Phones.NUMBER );
602
			do {
603
				Long id = cur.getLong( personIdCol );
604
				String number = sanitisePhoneNumber(
605
						cur.getString( numberCol ) );
606
				if( number != null ) {
607
					HashSet< String > numbers = _contactNumbers.get( id );
608
					if( numbers == null ) {
609
						_contactNumbers.put( id, new HashSet< String >() );
610
						numbers = _contactNumbers.get( id );
611
					}
612
					numbers.add( number );
613
				}
614
			} while( cur.moveToNext() );
615
		}
616
617
		// query and store map of contact ids to sets of email addresses
618
		cols = new String[] { Contacts.ContactMethods.PERSON_ID,
619
				Contacts.ContactMethods.DATA };
620
		cur = _doit.managedQuery( Contacts.ContactMethods.CONTENT_URI,
621
				cols, Contacts.ContactMethods.KIND + " = ?",
622
				new String[] { "" + Contacts.KIND_EMAIL }, null );
623
		if( cur.moveToFirst() ) {
624
			int personIdCol = cur.getColumnIndex(
625
					Contacts.ContactMethods.PERSON_ID );
626
			int addressCol = cur.getColumnIndex(
627
					Contacts.ContactMethods.DATA );
628
			do {
629
				Long id = cur.getLong( personIdCol );
630
				String address = sanitiseEmailAddress(
631
						cur.getString( addressCol ) );
632
				if( address != null ) {
633
					HashSet< String > addresses = _contactEmails.get( id );
634
					if( addresses == null ) {
635
						_contactEmails.put( id, new HashSet< String >() );
636
						addresses = _contactEmails.get( id );
637
					}
638
					addresses.add( address );
639
				}
640
			} while( cur.moveToNext() );
641
		}
642
	}
643
644
	private String sanitisePhoneNumber( String number )
645
	{
646
		number = number.replaceAll( "[-\\(\\) ]", "" );
647
		Pattern p = Pattern.compile( "^\\+?[0-9]+" );
648
		Matcher m = p.matcher( number );
649
		if( m.lookingAt() ) return m.group( 0 );
650
		return null;
651
	}
652
653
	private String sanitiseEmailAddress( String address )
654
	{
655
		address = address.trim();
656
		Pattern p = Pattern.compile(
657
				"^[^ @]+@[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?(\\.[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?)+$" );
658
		Matcher m = p.matcher( address );
659
		if( m.matches() ) {
660
			String[] bits = address.split( "@" );
661
			return bits[ 0 ] + "@" + bits[ 1 ].toLowerCase();
662
		}
663
		return null;
664
	}
665
}