All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rest4j.impl.MarshallerImpl Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.rest4j.impl;

import com.rest4j.*;
import com.rest4j.impl.model.CollectionType;
import com.rest4j.impl.model.FieldType;
import com.rest4j.impl.model.Model;
import com.rest4j.type.*;
import com.rest4j.json.JSONException;
import com.rest4j.json.JSONObject;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author Joseph Kapizza 
 */
public class MarshallerImpl implements Marshaller {
	Map models = new HashMap();
	ObjectFactoryChain objectFactoryChain = new ObjectFactoryChain() {
		@Nullable
		@Override
		public Object createInstance(@Nonnull String modelName, @Nonnull Class clz, @Nonnull JSONObject object) {
			Object inst;
			Class instantiate = getObjectType(modelName).getInstantiate();
			if (instantiate != null) {
				clz = instantiate;
			}
			try {
				inst = clz.newInstance();
			} catch (RuntimeException e) {
				throw e;
			} catch (Exception e) {
				// we have already checked instance creation in the configuration phase, so this should work
				throw new AssertionError(e);
			}
			return inst;
		}

	};
	ServiceProvider serviceProvider;
	FieldFilterChain fieldFilterChain = new FieldFilterChain() {
		@Override
		public Object marshal(Object json, Object parentObject, ObjectApiType parentType, com.rest4j.type.Field field) {
			return json;
		}

		@Override
		public Object unmarshal(Object json, Object parentObject, ObjectApiType parentType, com.rest4j.type.Field field) {
			return json;
		}
	};

	public static class ModelConfig {
		Model model;
		Object customMapper;

		public ModelConfig(Model model, Object customMapping) {
			this.model = model;
			this.customMapper = customMapping;
		}
	}

	MarshallerImpl(List modelConfigs) throws ConfigurationException {
		this(modelConfigs, new com.rest4j.ObjectFactory[0]);
	}

	MarshallerImpl(List modelConfigs, com.rest4j.ObjectFactory factory) throws ConfigurationException {
		this(modelConfigs, new com.rest4j.ObjectFactory[]{factory});
	}

	public MarshallerImpl(List modelConfig, ObjectFactory[] factories) throws ConfigurationException {
		this(modelConfig, factories, null);
	}

	MarshallerImpl(List modelConfigs, com.rest4j.ObjectFactory[] factories, ServiceProvider serviceProvider) throws ConfigurationException {
		this(modelConfigs, factories, new FieldFilter[0], serviceProvider);
	}

	MarshallerImpl(List modelConfigs, com.rest4j.ObjectFactory[] factories, FieldFilter[] fieldFilters, ServiceProvider serviceProvider) throws ConfigurationException {
		this.serviceProvider = serviceProvider;
		for (com.rest4j.ObjectFactory of: factories) {
			addObjectFactory(of);
		}
		for (FieldFilter ff: fieldFilters) {
			addFieldFilter(ff);
		}
		for (ModelConfig modelConfig : modelConfigs) {
			Model model = modelConfig.model;
			Object customMapper = modelConfig.customMapper;
			Class clz;
			try {
				clz = Class.forName(model.getClazz());
			} catch (ClassNotFoundException e) {
				throw new ConfigurationException("Cannot find class " + model.getClazz());
			}

			ObjectApiTypeImpl objectType = new ObjectApiTypeImpl(this, model.getName(), clz, model, customMapper, objectFactoryChain, fieldFilterChain, serviceProvider);
			if (model.getInstantiate() != null) {
				Class instantiate;
				try {
					instantiate = Class.forName(model.getInstantiate());
				} catch (ClassNotFoundException e) {
					throw new ConfigurationException("Cannot find class " + model.getInstantiate());
				}
				if (!clz.isAssignableFrom(instantiate)) {
					throw new ConfigurationException(clz+" should be assignable from "+model.getInstantiate());
				}
				try {
					// check that it can be instantiated
					instantiate.newInstance();
				} catch (InstantiationException e) {
					throw new ConfigurationException("Cannot instantiate " + model.getInstantiate()+": "+e.getMessage());
				} catch (IllegalAccessException e) {
					throw new ConfigurationException("Cannot instantiate " + model.getInstantiate()+": "+e.getMessage());
				}
				objectType.setInstantiate(instantiate);
			}
			models.put(model.getName(), objectType);
		}

		// fill model interconnections and type-check
		for (ModelConfig modelConfig : modelConfigs) {
			Model model = modelConfig.model;
			ObjectApiTypeImpl modelImpl = models.get(model.getName());
			modelImpl.link();
		}
	}

	@Override
	public ObjectApiTypeImpl getObjectType(String model) {
		return models.get(model);
	}

	@Override
	public ArrayApiTypeImpl getArrayType(ApiType type) {
		return new ArrayApiTypeImpl(this, type);
	}

	@Override
	public MapApiTypeImpl getMapType(ApiType type) {
		return new MapApiTypeImpl(this, type);
	}

	@Override
	public StringApiType getStringType(String[] values) {
		return new StringApiTypeImpl(this, values);
	}

	@Override
	public NumberApiType getNumberType() {
		return new NumberApiTypeImpl(this);
	}

	@Override
	public BooleanApiType getBooleanType() {
		return new BooleanApiTypeImpl(this);
	}

	@Override
	public DateApiType getDateType() {
		return new DateApiTypeImpl(this);
	}

	@Override
	public Object marshal(ApiType apiType, Object value) throws ApiException {
		if (apiType instanceof ConcreteClassMapping) apiType = ((ConcreteClassMapping)apiType).objectApiType;
		return ((ApiTypeImpl)apiType).marshal(value);
	}

	@Override
	public Object unmarshal(ApiType apiType, Object value) throws ApiException {
		if (apiType instanceof ConcreteClassMapping) apiType = ((ConcreteClassMapping)apiType).objectApiType;
		return ((ApiTypeImpl)apiType).unmarshal(value);
	}

	@Override
	public Object unmarshalPatch(PatchableType apiType, Object original, JSONObject object) throws ApiException {
		if (apiType instanceof ConcreteClassMapping) apiType = ((ConcreteClassMapping)apiType).objectApiType;
		return apiType.unmarshalPatch(original, object);
	}

	SimpleApiType createSimpleType(FieldType type, String[] enumValues) {
		SimpleApiType apiType;
		switch (type) {
			case STRING:
				apiType = getStringType(enumValues);
				break;
			case NUMBER:
				apiType = getNumberType();
				break;
			case BOOLEAN:
				apiType = getBooleanType();
				break;
			case DATE:
				apiType = getDateType();
				break;
			default:
				throw new AssertionError();

		}
		return apiType;
	}

	ApiType createType(CollectionType collection, ApiType elementType) {
		switch (collection) {
			case ARRAY:
				return getArrayType(elementType);
			case SINGLETON:
				return elementType;
			case MAP:
				return getMapType(elementType);
			default:
				throw new AssertionError();
		}
	}

	Class getClassForModel(String model) {
		return models.get(model).clz;
	}

	static private final Pattern ISDOUBLE = Pattern.compile("[\\.eE]");

	Object parse(String val, FieldType type) {
		if (val == null) return null;
		switch (type) {
			case BOOLEAN: return Boolean.parseBoolean(val);
			case NUMBER:
				if (ISDOUBLE.matcher(val).find()) {
					return Double.parseDouble(val);
				} else {
					return Long.parseLong(val);
				}
			case STRING: return val;
		}
		throw new AssertionError();
	}

	static void checkEnum(Class clz, String[] enumValues) throws ConfigurationException {
		// check that the declared enum values are a subset of java enum
		for (String declaredValue: enumValues) {
			boolean found = false;
			for (Object option: clz.getEnumConstants()) {
				if (((Enum)option).name().equals(declaredValue)) found = true;
			}
			if (!found) {
				throw new ConfigurationException("Cannot find a enum value "+declaredValue+" in "+clz);
			}
		}
	}

	private void addObjectFactory(final com.rest4j.ObjectFactory factory) {
		final ObjectFactoryChain nextChain = objectFactoryChain;
		objectFactoryChain = new ObjectFactoryChain() {

			@Nullable
			@Override
			public Object createInstance(@Nonnull String modelName, @Nonnull Class clz, @Nonnull JSONObject object) throws JSONException, ApiException {
				return factory.createInstance(modelName, clz, object, nextChain);
			}

		};
	}

	private void addFieldFilter(final FieldFilter ff) {
		final FieldFilterChain nextChain = fieldFilterChain;
		fieldFilterChain = new FieldFilterChain() {
			@Override
			public Object marshal(Object json, Object parentObject, ObjectApiType parentType, Field field) {
				return ff.marshal(json, parentObject, parentType, field, nextChain);
			}

			@Override
			public Object unmarshal(Object json, Object parentObject, ObjectApiType parentType, Field field) {
				return ff.unmarshal(json, parentObject, parentType, field, nextChain);
			}
		};
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy