summaryrefslogtreecommitdiffstats
path: root/qtjava/javalib/org/trinitydesktop/qt/qtjava.java
blob: f7afcee954812d1a0dbb9a2fc5e1c71b823ad8c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/***************************************************************************
                            qtjava.java -  description
                             -------------------
    begin                : Tue Oct 31 06:12:14 2000
    copyright            : (C) 2000 Lost Highway Ltd. All rights reserved.
    email                : Lost_Highway@tipitina.demon.co.uk
    written by           : Richard Dale.
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

package org.trinitydesktop.qt;

import java.util.*;
import java.lang.Error;

/** The 'Run the TQt Java library' class'. Various utility methods to manage
	the mapping between C++ and java instances. Used in conjunction the C++ methods
	in QtSupport.cpp and JavaSlot.cpp.

	@author Richard Dale <Richard_Dale@tipitina.demon.co.uk>
	*/
public class qtjava {
	/** Uses a C++ key to retrieve the corresponding Java instance */
	public static WeakValueMap	qtKeyToJavaMap = null;

	/** Allows a JavaSignal proxy instance to be retrieved for a given Java
		instance/Signal name combination */
	public static HashMap 		qtSignalDictionary = null;

	/** Allows a JavaSlot proxy instance to be retrieved for a given Java
		instance/Slot name combination */
	public static HashMap 		qtSlotDictionary = null;

	/** Register a JavaVM pointer to make it easy to retrieve the current
		JNIEnv later */
	private native static void registerJVM();

	/** Get/set the C++ instance for a given Java instance */
	private native static void setQt(Object obj, long qt);
	private native static long getQt(Object obj);

	/** If a C++ instance has been allocated in the Java World, it will
		be deleted within the corresponding Java instance's finalize method.  */
	private native static void setAllocatedInJavaWorld(Object obj, boolean yn);
	private native static boolean allocatedInJavaWorld(Object obj);

	/** This member allows a typecast of an instance which wraps a
		Qt instance, to a more specialized type. */
	private static QtSupport dynamicCast(String type, QtSupport source) {
		boolean sourceAllocatedInJavaWorld	= allocatedInJavaWorld(source);
		long	qtHandle					= getQt(source);

		removeObjectForQtKey(qtHandle);
		setAllocatedInJavaWorld(source, false);
		return (QtSupport) objectForQtKey(qtHandle, type, sourceAllocatedInJavaWorld);
	}

	/** Add a 'C++ qt instance key/Java instance value' pair to the map */
	public static void setObjectForQtKey(Object obj, long qt) {
		qtKeyToJavaMap.put(Long.toString(qt).intern(), obj);
		return;
	}

	/** Remove a 'C++ qt instance key/Java instance value' pair from the map.
		Normally an entry would be removed when its map value is the last reference
		left to the java instance, and it becomes a weak reference to be reaped.
		But C++ can reuse a heap address for a C++ ref without giving the java runtime
		a chance to do any garbage collection and tidy up the corresponding entry in the
		qtKeyToJavaMap (tricky!).

		So it is useful to be able to force the early removal of an entry, when the C++
		instance has a known lifetime (eg a TQEvent after its event handler has
		returned).
	 */
	public static void removeObjectForQtKey(long qt) {
		qtKeyToJavaMap.remove(Long.toString(qt).intern());
		return;
	}

	/** Allocates an instance of a class without initializing it */
	private native static Object allocateInstance(Class cls) throws InstantiationException, IllegalAccessException;

	/** If the C++ qt instance is an instance of TQObject, then
		use the Qt runtime meta-data to retrieve the class name.
		If there is a Java class with the same name, then return
		a Class object for that class. Otherwise, just return the
		original 'approximate' Class passed to the method.

		Note that a java equivalent of the C++ instance doesn't have
		to be instantiated to do this */
	private native static Class classFromQtMetaData(Class approximateClass, String approximateClassName, long qt);

	/** Retrieves a corresponding Java instance for a given C++ instance. Allocates
		the Java instance if it doesn't already exist. */
	public static Object objectForQtKey(long qt, String className, boolean allocatedInJavaWorld) {
		Object	result;
		Class	aClass;

		result = qtKeyToJavaMap.get(Long.toString(qt).intern());

		if (result == null) {
			try {
				aClass = Class.forName(toFullyQualifiedClassName(className));
			} catch (ClassNotFoundException e) {
				TQt.tqWarning("Error class not found: " + toFullyQualifiedClassName(className));
				return null;
			}

			if (QtSupport.class.isAssignableFrom(aClass)) {
				if (TQObject.class.isAssignableFrom(aClass)) {
					aClass = qtjava.classFromQtMetaData(aClass, aClass.getName(), qt);
				}

				try {
					result = qtjava.allocateInstance(aClass);
				} catch (InstantiationException e) {
					TQt.tqWarning("Can't instantiate : " + toFullyQualifiedClassName(className));
					return null;
				} catch (IllegalAccessException e) {
					TQt.tqWarning("Illegal access to class : " + toFullyQualifiedClassName(className));
					return null;
				}

				setQt(result, qt);
				setAllocatedInJavaWorld(result, allocatedInJavaWorld);
			} else {
				// A Java instance without a wrapped Qt C++ instance (eg a list)
				try {
					result = aClass.newInstance();
				} catch (InstantiationException e) {
					return null;
				} catch (IllegalAccessException e) {
					return null;
				}
			}

			setObjectForQtKey(result, qt);
		}

		return result;
	}

	/** When a C++ instance has been deleted. Retrieves a corresponding Java instance for a given C++ instance. Sets
		the '_allocatedInJavaWorld' flag to false. */
	public static void qtKeyDeleted(long qt) {
		Object result = qtKeyToJavaMap.get(Long.toString(qt).intern());

		if (	result != null
				&& QtSupport.class.isAssignableFrom(result.getClass()) )
		{
			setAllocatedInJavaWorld(result, false);
		}
	}

	/** Converts any unqualified class names in a signal or slot string to the fully qualified versions */
	public static String toFullyQualifiedClassName(String className) {
		if (className.equals("String")) {
			return "java.lang.String";
		} else if (className.equals("Date")) {
			return "java.util.Date";
		} else if (className.equals("Calendar")) {
			return "java.util.GregorianCalendar";
		} else if (className.equals("ArrayList")) {
			return "java.util.ArrayList";
		} else if (className.equals("ByteArrayOutputStream")) {
			return "java.io.ByteArrayOutputStream";
		} else if (className.equals("Job")) {
			return "org.trinitydesktop.koala.Job";
		} else if (className.equals("Part")) {
			return "org.trinitydesktop.koala.Part";
		} else if (className.equals("Slave")) {
			return "org.trinitydesktop.koala.Slave";
		} else if (className.equals("DOMNode")) {
			return "org.trinitydesktop.koala.DOMNode";
		} else if (className.startsWith("Q")) {
			return "org.trinitydesktop.qt." + className;
		} else if (className.startsWith("K")) {
			return "org.trinitydesktop.koala." + className;
		}
		return className;
	}

	/** Converts from a list Java types in a signal or slot string, to the fully qualified equivalent */
	private static String toNormalizedTypeSignature(String typeSignature) {
		StringBuffer normalizedTypeSignature = new StringBuffer(	typeSignature.substring(	0,
																	typeSignature.indexOf('(') + 1 ) );
		StringTokenizer tokenizer = new StringTokenizer(	typeSignature.substring(	typeSignature.indexOf('(') + 1,
																						typeSignature.indexOf(')') ),
															"," );

		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken();

			if (	token.equals("boolean")
					|| token.equals("byte")
					|| token.equals("char")
					|| token.equals("long")
					|| token.equals("int")
					|| token.equals("short")
					|| token.equals("float")
					|| token.equals("double") )
			{
				normalizedTypeSignature.append(token);
			} else {
				normalizedTypeSignature.append(toFullyQualifiedClassName(token));
			}

			if (tokenizer.hasMoreTokens()) {
				normalizedTypeSignature.append(",");
			}
		}

		normalizedTypeSignature.append(")");
		return normalizedTypeSignature.toString();
	}

	/** Returns a new C++ JavaSignal proxy instance, to forward the signal
		onto the target Java slot */
	private native static long newJavaSignal();

	/** Looks up a 'qt instance/signal name' key and returns the corresponding
		JavaSignal instance */
	public static long signalForSender(long qt, String signal) {
		String normalizedSignal = toNormalizedTypeSignature(signal);
		String key = (Long.toString(qt) +  normalizedSignal).intern();
		Long result = (Long) qtSignalDictionary.get(key);

		if (result == null) {
			long javaSignal = newJavaSignal();
			qtSignalDictionary.put(key, new Long(javaSignal));
			return javaSignal;
		} else {
			return result.longValue();
		}
	}

	/** Initialises the JavaSlot factory */
	private native static void setJavaSlotFactory();

	/** Returns a new C++ JavaSlot proxy instance, to receive signals
		and invoke the target Java slot */
	private native static long newJavaSlot(TQObject receiver, String member);

	/** Looks up a 'qt instance/slot name' key and returns the corresponding
		JavaSlot instance */
	public static long slotForReceiver(long qt, TQObject receiver, String slot) {
		String normalizedSlot = toNormalizedTypeSignature(slot);
		String key = (Long.toString(qt) +  normalizedSlot).intern();
		Long result = (Long) qtSlotDictionary.get(key);

		if (result == null) {
			long javaSlot = newJavaSlot(receiver, normalizedSlot);
			qtSlotDictionary.put(key, new Long(javaSlot));
			return javaSlot;
		} else {
			return result.longValue();
		}
	}

	private static boolean _initialized = false;

	public static void initialize() {
		if (!_initialized) {
			System.loadLibrary("tqtjava");
			qtjava.registerJVM();
			qtjava.setJavaSlotFactory();
			qtKeyToJavaMap = new WeakValueMap();
			qtSignalDictionary = new HashMap();
			qtSlotDictionary = new HashMap();
			_initialized = true;
		}
	}

	static {
		initialize();
	}
}