1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org 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 Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_connectivity.hxx"
30 
31 #include "MacabRecord.hxx"
32 #include "macabutilities.hxx"
33 #include <com/sun/star/util/DateTime.hpp>
34 
35 #include <premac.h>
36 #include <Carbon/Carbon.h>
37 #include <AddressBook/ABAddressBookC.h>
38 #include <postmac.h>
39 #include <connectivity/dbconversion.hxx>
40 
41 using namespace connectivity::macab;
42 using namespace com::sun::star::util;
43 using namespace ::dbtools;
44 
45 // -------------------------------------------------------------------------
46 MacabRecord::MacabRecord()
47 {
48 	size = 0;
49 	fields = NULL;
50 }
51 
52 // -------------------------------------------------------------------------
53 MacabRecord::MacabRecord(const sal_Int32 _size)
54 {
55 	size = _size;
56 	fields = new macabfield *[size];
57 	sal_Int32 i;
58 	for(i = 0; i < size; i++)
59 		fields[i] = NULL;
60 }
61 
62 // -------------------------------------------------------------------------
63 MacabRecord::~MacabRecord()
64 {
65 	if(size > 0)
66 	{
67 		int i;
68 		for(i = 0; i < size; i++)
69 		{
70 			delete fields[i];
71 			fields[i] = NULL;
72 		}
73 	}
74 	delete [] fields;
75 	fields = NULL;
76 }
77 
78 // -------------------------------------------------------------------------
79 void MacabRecord::insertAtColumn (CFTypeRef _value, ABPropertyType _type, const sal_Int32 _column)
80 {
81 	if(_column < size)
82 	{
83 		if(fields[_column] == NULL)
84 			fields[_column] = new macabfield;
85 
86 		fields[_column]->value = _value;
87 		if (fields[_column]->value)
88 			CFRetain(fields[_column]->value);
89 		fields[_column]->type = _type;
90 	}
91 }
92 
93 // -------------------------------------------------------------------------
94 sal_Bool MacabRecord::contains (const macabfield *_field) const
95 {
96 	if(_field == NULL)
97 		return sal_False;
98 	else
99 		return contains(_field->value);
100 }
101 
102 // -------------------------------------------------------------------------
103 sal_Bool MacabRecord::contains (const CFTypeRef _value) const
104 {
105 	sal_Int32 i;
106 	for(i = 0; i < size; i++)
107 	{
108 		if(fields[i] != NULL)
109 		{
110 			if(CFEqual(fields[i]->value, _value))
111 			{
112 				return sal_True;
113 			}
114 		}
115 	}
116 
117 	return sal_False;
118 }
119 
120 // -------------------------------------------------------------------------
121 sal_Int32 MacabRecord::getSize() const
122 {
123 	return size;
124 }
125 
126 // -------------------------------------------------------------------------
127 macabfield *MacabRecord::copy(const sal_Int32 i) const
128 {
129 	/* Note: copy(i) creates a new macabfield identical to that at
130 	 * location i, whereas get(i) returns a pointer to the macabfield
131 	 * at location i.
132 	 */
133 	if(i < size)
134 	{
135 		macabfield *_copy = new macabfield;
136 		_copy->type = fields[i]->type;
137 		_copy->value = fields[i]->value;
138 		if (_copy->value)
139 			CFRetain(_copy->value);
140 		return _copy;
141 	}
142 
143 	return NULL;
144 }
145 
146 // -------------------------------------------------------------------------
147 macabfield *MacabRecord::get(const sal_Int32 i) const
148 {
149 	/* Note: copy(i) creates a new macabfield identical to that at
150 	 * location i, whereas get(i) returns a pointer to the macabfield
151 	 * at location i.
152 	 */
153 	if(i < size)
154 	{
155 		return fields[i];
156 	}
157 
158 	return NULL;
159 }
160 
161 // -------------------------------------------------------------------------
162 void MacabRecord::releaseFields()
163 {
164 	/* This method is, at the moment, only used in MacabHeader.cxx, but
165 	 * the idea is simple: if you are not destroying this object but want
166 	 * to clear it of its macabfields, you should release each field's
167 	 * value.
168 	 */
169 	sal_Int32 i;
170 	for(i = 0; i < size; i++)
171 		CFRelease(fields[i]->value);
172 }
173 
174 // -------------------------------------------------------------------------
175 sal_Int32 MacabRecord::compareFields(const macabfield *_field1, const macabfield *_field2)
176 {
177 
178 	/* When comparing records, if either field is NULL (and the other is
179 	 * not), that field is considered "greater than" the other, so that it
180 	 * shows up later in the list when fields are ordered.
181 	 */
182 	if(_field1 == _field2)
183 		return 0;
184 	if(_field1 == NULL)
185 		return 1;
186 	if(_field2 == NULL)
187 		return -1;
188 
189 	/* If they aren't the same type, for now, return the one with
190 	 * the smaller type ID... I don't know of a better way to compare
191 	 * two different data types.
192 	 */
193 	if(_field1->type != _field2->type)
194 		return(_field1->type - _field2->type);
195 
196 	CFComparisonResult result;
197 
198 	/* Carbon has a unique compare function for each data type: */
199 	switch(_field1->type)
200 	{
201 		case kABStringProperty:
202 			result = CFStringCompare(
203 				(CFStringRef) _field1->value,
204 				(CFStringRef) _field2->value,
205 				kCFCompareLocalized); // Specifies that the comparison should take into account differences related to locale, such as the thousands separator character.
206 			break;
207 
208 		case kABDateProperty:
209 			result = CFDateCompare(
210 				(CFDateRef) _field1->value,
211 				(CFDateRef) _field2->value,
212 				NULL); // NULL = unused variable
213 			break;
214 
215 		case kABIntegerProperty:
216 		case kABRealProperty:
217 			result = CFNumberCompare(
218 				(CFNumberRef) _field1->value,
219 				(CFNumberRef) _field2->value,
220 				NULL); // NULL = unused variable
221 		break;
222 
223 		default:
224 			result = kCFCompareEqualTo; // can't compare
225 	}
226 
227 	return (sal_Int32) result;
228 }
229 
230 // -------------------------------------------------------------------------
231 /* Create a macabfield out of an OUString and type. Together with the
232  * method fieldToString() (below), it is possible to switch conveniently
233  * between an OUString and a macabfield (for use when creating and handling
234  * SQL statement).
235  */
236 macabfield *MacabRecord::createMacabField(const ::rtl::OUString _newFieldString, const ABPropertyType _abType)
237 {
238 	macabfield *newField = NULL;
239 	switch(_abType)
240 	{
241 		case kABStringProperty:
242 			newField = new macabfield;
243 			newField->value = OUStringToCFString(_newFieldString);
244 			newField->type = _abType;
245 			break;
246 		case kABDateProperty:
247 			{
248 				DateTime aDateTime = DBTypeConversion::toDateTime(_newFieldString);
249 
250 				// bad format...
251 				if(aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0)
252 				{
253 				}
254 				else
255 				{
256 					double nTime = DBTypeConversion::toDouble(aDateTime, DBTypeConversion::getStandardDate());
257 					nTime -= kCFAbsoluteTimeIntervalSince1970;
258 					newField = new macabfield;
259 					newField->value = CFDateCreate(NULL, (CFAbsoluteTime) nTime);
260 					newField->type = _abType;
261 				}
262 			}
263 			break;
264 		case kABIntegerProperty:
265 			try
266 			{
267 				sal_Int64 nVal = _newFieldString.toInt64();
268 
269 				newField = new macabfield;
270 				newField->value = CFNumberCreate(NULL,kCFNumberLongType, &nVal);
271 				newField->type = _abType;
272 			}
273 			// bad format...
274 			catch(...)
275 			{
276 			}
277 			break;
278 		case kABRealProperty:
279 			try
280 			{
281 				double nVal = _newFieldString.toDouble();
282 
283 				newField = new macabfield;
284 				newField->value = CFNumberCreate(NULL,kCFNumberDoubleType, &nVal);
285 				newField->type = _abType;
286 			}
287 			// bad format...
288 			catch(...)
289 			{
290 			}
291 			break;
292 		default:
293 			;
294 	}
295 	return newField;
296 }
297 
298 // -------------------------------------------------------------------------
299 /* Create an OUString out of a macabfield. Together with the method
300  * createMacabField() (above), it is possible to switch conveniently
301  * between an OUString and a macabfield (for use when creating and handling
302  * SQL statement).
303  */
304 ::rtl::OUString MacabRecord::fieldToString(const macabfield *_aField)
305 {
306 	if(_aField == NULL)
307 		return ::rtl::OUString();
308 
309 	::rtl::OUString fieldString;
310 
311 	switch(_aField->type)
312 	{
313 		case kABStringProperty:
314 			fieldString = CFStringToOUString((CFStringRef) _aField->value);
315 			break;
316 		case kABDateProperty:
317 			{
318 				DateTime aTime = CFDateToDateTime((CFDateRef) _aField->value);
319 				fieldString = DBTypeConversion::toDateTimeString(aTime);
320 			}
321 			break;
322 		case kABIntegerProperty:
323 			{
324 				CFNumberType numberType = CFNumberGetType( (CFNumberRef) _aField->value );
325 				sal_Int64 nVal;
326 				// Should we check for the wrong type here, e.g., a float?
327 				sal_Bool m_bSuccess = !CFNumberGetValue((CFNumberRef) _aField->value, numberType, &nVal);
328 				if(m_bSuccess != sal_False)
329 					fieldString = ::rtl::OUString::valueOf(nVal);
330 			}
331 			break;
332 		case kABRealProperty:
333 			{
334 				CFNumberType numberType = CFNumberGetType( (CFNumberRef) _aField->value );
335 				double nVal;
336 				// Should we check for the wrong type here, e.g., an int?
337 				sal_Bool m_bSuccess = !CFNumberGetValue((CFNumberRef) _aField->value, numberType, &nVal);
338 				if(m_bSuccess != sal_False)
339 					fieldString = ::rtl::OUString::valueOf(nVal);
340 			}
341 			break;
342 		default:
343 			;
344 	}
345 	return fieldString;
346 
347 }
348