/*
* Copyright 2007 Yusuke Yamamoto
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package twitter4j.json;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import twitter4j.AccountTotals;
import twitter4j.Category;
import twitter4j.DirectMessage;
import twitter4j.IDs;
import twitter4j.Location;
import twitter4j.OEmbed;
import twitter4j.Place;
import twitter4j.RateLimitStatus;
import twitter4j.Relationship;
import twitter4j.SavedSearch;
import twitter4j.Status;
import twitter4j.StatusDeletionNotice;
import twitter4j.Trend;
import twitter4j.Trends;
import twitter4j.TwitterException;
import twitter4j.User;
import twitter4j.UserList;
/**
* @author Yusuke Yamamoto - yusuke at mac.com
* @since Twitter4J 2.1.7
*/
@SuppressWarnings("unchecked")
public final class DataObjectFactory {
private static final Constructor<Status> statusConstructor;
private static final Constructor<User> userConstructor;
private static final Constructor<Relationship> relationshipConstructor;
private static final Constructor<Place> placeConstructor;
private static final Constructor<SavedSearch> savedSearchConstructor;
private static final Constructor<Trend> trendConstructor;
private static final Constructor<Trends> trendsConstructor;
private static final Constructor<IDs> IDsConstructor;
private static final Method rateLimitStatusConstructor;
private static final Constructor<Category> categoryConstructor;
private static final Constructor<DirectMessage> directMessageConstructor;
private static final Constructor<Location> locationConstructor;
private static final Constructor<UserList> userListConstructor;
private static final Constructor<StatusDeletionNotice> statusDeletionNoticeConstructor;
private static final Constructor<AccountTotals> accountTotalsConstructor;
private static final Constructor<OEmbed> oembedConstructor;
static {
try {
statusConstructor = (Constructor<Status>) Class.forName("twitter4j.internal.json.StatusJSONImpl")
.getDeclaredConstructor(JSONObject.class);
statusConstructor.setAccessible(true);
userConstructor = (Constructor<User>) Class.forName("twitter4j.internal.json.UserJSONImpl")
.getDeclaredConstructor(JSONObject.class);
userConstructor.setAccessible(true);
relationshipConstructor = (Constructor<Relationship>) Class.forName(
"twitter4j.internal.json.RelationshipJSONImpl").getDeclaredConstructor(JSONObject.class);
relationshipConstructor.setAccessible(true);
placeConstructor = (Constructor<Place>) Class.forName("twitter4j.internal.json.PlaceJSONImpl")
.getDeclaredConstructor(JSONObject.class);
placeConstructor.setAccessible(true);
savedSearchConstructor = (Constructor<SavedSearch>) Class.forName(
"twitter4j.internal.json.SavedSearchJSONImpl").getDeclaredConstructor(JSONObject.class);
savedSearchConstructor.setAccessible(true);
trendConstructor = (Constructor<Trend>) Class.forName("twitter4j.internal.json.TrendJSONImpl")
.getDeclaredConstructor(JSONObject.class);
trendConstructor.setAccessible(true);
trendsConstructor = (Constructor<Trends>) Class.forName("twitter4j.internal.json.TrendsJSONImpl")
.getDeclaredConstructor(String.class);
trendsConstructor.setAccessible(true);
IDsConstructor = (Constructor<IDs>) Class.forName("twitter4j.internal.json.IDsJSONImpl")
.getDeclaredConstructor(String.class);
IDsConstructor.setAccessible(true);
rateLimitStatusConstructor = Class.forName("twitter4j.internal.json.RateLimitStatusJSONImpl")
.getDeclaredMethod("createRateLimitStatuses", JSONObject.class);
rateLimitStatusConstructor.setAccessible(true);
categoryConstructor = (Constructor<Category>) Class.forName("twitter4j.internal.json.CategoryJSONImpl")
.getDeclaredConstructor(JSONObject.class);
categoryConstructor.setAccessible(true);
directMessageConstructor = (Constructor<DirectMessage>) Class.forName(
"twitter4j.internal.json.DirectMessageJSONImpl").getDeclaredConstructor(JSONObject.class);
directMessageConstructor.setAccessible(true);
locationConstructor = (Constructor<Location>) Class.forName("twitter4j.internal.json.LocationJSONImpl")
.getDeclaredConstructor(JSONObject.class);
locationConstructor.setAccessible(true);
userListConstructor = (Constructor<UserList>) Class.forName("twitter4j.internal.json.UserListJSONImpl")
.getDeclaredConstructor(JSONObject.class);
userListConstructor.setAccessible(true);
statusDeletionNoticeConstructor = (Constructor<StatusDeletionNotice>) Class.forName(
"twitter4j.StatusDeletionNoticeImpl").getDeclaredConstructor(JSONObject.class);
statusDeletionNoticeConstructor.setAccessible(true);
accountTotalsConstructor = (Constructor<AccountTotals>) Class.forName(
"twitter4j.internal.json.AccountTotalsJSONImpl").getDeclaredConstructor(JSONObject.class);
accountTotalsConstructor.setAccessible(true);
oembedConstructor = (Constructor<OEmbed>) Class.forName("twitter4j.internal.json.OEmbedJSONImpl")
.getDeclaredConstructor(JSONObject.class);
oembedConstructor.setAccessible(true);
} catch (final NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
} catch (final ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
@SuppressWarnings("rawtypes")
private static final ThreadLocal<Map> rawJsonMap = new ThreadLocal<Map>() {
@Override
protected Map initialValue() {
return new HashMap<Object, Object>();
}
};
private DataObjectFactory() {
throw new AssertionError("not intended to be instantiated.");
}
/**
* Constructs an AccountTotals object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return AccountTotals
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.9
*/
public static AccountTotals createAccountTotals(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return accountTotalsConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Category object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Category
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Category createCategory(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return categoryConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a DirectMessage object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return DirectMessage
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static DirectMessage createDirectMessage(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return directMessageConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a IDs object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return IDs
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static IDs createIDs(final String rawJSON) throws TwitterException {
try {
return IDsConstructor.newInstance(rawJSON);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Location object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Location
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Location createLocation(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return locationConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Construct an object from rawJSON string. This method may be called when
* you do not know what a given raw JSON string contains. It will do the
* work of determining what type of object the JSON represents, and
* constructing the respective object type. For example, if the JSON
* contents represents a Status, then a Status will be returned. If it
* represents a deletion notice, then a StatusDeletionNotice will be
* returned. The caller can simply use instanceof to handle the returned
* object as applicable. NOTE: the raw JSONObject will be returned in cases
* where there isn't a discrete respective object type that can be
* constructed. That way, the caller can at least have access to the JSON
* itself.
*
* @param rawJSON raw JSON form as String
* @return the respective constructed object, or the JSONObject in the case
* where we cannot determine the object type.
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.9
*/
public static Object createObject(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
final JSONObjectType.Type jsonObjectType = JSONObjectType.determine(json);
switch (jsonObjectType) {
case SENDER:
return registerJSONObject(
directMessageConstructor.newInstance(json.getJSONObject("direct_message")), json);
case STATUS:
return registerJSONObject(statusConstructor.newInstance(json), json);
case DIRECT_MESSAGE:
return registerJSONObject(
directMessageConstructor.newInstance(json.getJSONObject("direct_message")), json);
case DELETE:
return registerJSONObject(
statusDeletionNoticeConstructor.newInstance(json.getJSONObject("delete").getJSONObject(
"status")), json);
case LIMIT:
// TODO: Perhaps there should be a TrackLimitationNotice
// object?
// The onTrackLimitationNotice method could take that as an
// arg.
return json;
case SCRUB_GEO:
return json;
default:
// The object type is unrecognized...just return the json
return json;
}
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs an OEmbed object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return OEmbed
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 3.0.2
*/
public static OEmbed createOEmbed(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return oembedConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Place object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Place
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Place createPlace(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return placeConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a RateLimitStatus object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return RateLimitStatus
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Map<String, RateLimitStatus> createRateLimitStatus(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return (Map<String, RateLimitStatus>) rateLimitStatusConstructor.invoke(
Class.forName("twitter4j.internal.json.RateLimitStatusJSONImpl"), json);
} catch (final ClassNotFoundException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Relationship object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Relationship
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Relationship createRelationship(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return relationshipConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a SavedSearch object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return SavedSearch
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static SavedSearch createSavedSearch(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return savedSearchConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Status object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Status
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Status createStatus(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return statusConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Trend object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Trend
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Trend createTrend(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return trendConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a Trends object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return Trends
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static Trends createTrends(final String rawJSON) throws TwitterException {
try {
return trendsConstructor.newInstance(rawJSON);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new TwitterException(e);
} catch (final InvocationTargetException e) {
throw new AssertionError(e);
}
}
/**
* Constructs a User object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return User
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static User createUser(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return userConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Constructs a UserList object from rawJSON string.
*
* @param rawJSON raw JSON form as String
* @return UserList
* @throws TwitterException when provided string is not a valid JSON string.
* @since Twitter4J 2.1.7
*/
public static UserList createUserList(final String rawJSON) throws TwitterException {
try {
final JSONObject json = new JSONObject(rawJSON);
return userListConstructor.newInstance(json);
} catch (final InstantiationException e) {
throw new TwitterException(e);
} catch (final IllegalAccessException e) {
throw new AssertionError(e);
} catch (final InvocationTargetException e) {
throw new TwitterException(e);
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
/**
* Returns a raw JSON form of the provided object.<br>
* Note that raw JSON forms can be retrieved only from the same thread
* invoked the last method call and will become inaccessible once another
* method call
*
* @param obj
* @return raw JSON
* @since Twitter4J 2.1.7
*/
public static String getRawJSON(final Object obj) {
final Object json = rawJsonMap.get().get(obj);
if (json instanceof String)
return (String) json;
else if (json != null) // object must be instance of JSONObject
return json.toString();
else
return null;
}
/**
* clear raw JSON forms associated with the current thread.<br>
* Currently this method is called indirectly by
* twitter4j.internal.util.DataObjectFactoryUtil, and should be called
* directly once *JSONImpl classes are migrated to twitter4j.json.* package.
*
* @since Twitter4J 2.1.7
*/
static void clearThreadLocalMap() {
rawJsonMap.get().clear();
}
/**
* associate a raw JSON form to the current thread<br>
* Currently this method is called indirectly by
* twitter4j.internal.util.DataObjectFactoryUtil, and should be called
* directly once *JSONImpl classes are migrated to twitter4j.json.* package.
*
* @since Twitter4J 2.1.7
*/
static <T> T registerJSONObject(final T key, final Object json) {
rawJsonMap.get().put(key, json);
return key;
}
}