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

net.officefloor.web.value.retrieve.RootValueRetrieverImpl Maven / Gradle / Ivy

There is a newer version: 3.40.0
Show newest version
/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2018 Daniel Sagenschneider
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package net.officefloor.web.value.retrieve;

import java.util.HashMap;
import java.util.Map;

import net.officefloor.server.http.HttpException;

/**
 * {@link ValueRetriever} implementation.
 * 
 * @author Daniel Sagenschneider
 */
public class RootValueRetrieverImpl implements ValueRetriever {

	/**
	 * Mapping of the property name to the {@link RetrieverStruct}.
	 */
	private final Map propertyToRetriever = new HashMap();

	/**
	 * Indicates if case insensitive.
	 */
	private final boolean isCaseInsensitive;

	/**
	 * Initiate.
	 * 
	 * @param properties
	 *            {@link PropertyMetaData} instances.
	 * @param isCaseInsensitive
	 *            Indicates if case insensitive.
	 */
	public RootValueRetrieverImpl(PropertyMetaData[] properties, boolean isCaseInsensitive) {
		this(properties, isCaseInsensitive, new HashMap>());
	}

	/**
	 * Initiate.
	 * 
	 * @param properties
	 *            {@link PropertyMetaData} instances.
	 * @param isCaseInsensitive
	 *            Indicates if case insensitive.
	 * @param valueRetrieverByMetaData
	 *            {@link ValueRetriever} by its {@link PropertyMetaData}.
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	RootValueRetrieverImpl(PropertyMetaData[] properties, boolean isCaseInsensitive,
			Map> valueRetrieverByMetaData) {
		this.isCaseInsensitive = isCaseInsensitive;

		// Load the property retriever
		for (PropertyMetaData property : properties) {

			// Obtain the property name
			String propertyName = property.getPropertyName();

			// Determine if already registered value retriever
			ValueRetriever propertyRetriever = valueRetrieverByMetaData.get(property);
			if (propertyRetriever == null) {
				// Create the property retriever (registers itself)
				propertyRetriever = new PropertyValueRetrieverImpl(property, this.isCaseInsensitive,
						valueRetrieverByMetaData);
			}

			// Register the property retriever
			this.propertyToRetriever.put(propertyName, new RetrieveStruct(propertyRetriever, property));
		}
	}

	/*
	 * ===================== ValueRetreiver ======================
	 */

	/**
	 * {@link Processor} to obtain value type.
	 */
	private static Processor> valueTypeRetriever = (propertyRetriever, object, remainingName) -> {

		// Must be retriever to obtain value
		if (propertyRetriever == null) {
			return null; // property not retrievable
		}

		// Determine if further property
		if (remainingName.length() == 0) {
			// No further properties, so provide value type
			return propertyRetriever.metaData.getValueType();
		}

		// Delegate to obtain type method
		return propertyRetriever.retriever.getValueType(remainingName);
	};

	@Override
	public Class getValueType(String name) throws HttpException {
		// Return type method
		return this.process(name, null, valueTypeRetriever);
	}

	@Override
	public  A getValueAnnotation(String name, Class annotationType) throws HttpException {
		return this.process(name, null, (propertyRetriever, object, remainingName) -> {

			// Must be retriever to obtain value
			if (propertyRetriever == null) {
				return null; // no property, no annotation
			}

			// Determine if further property
			if (remainingName.length() == 0) {
				// No further properties, so provide value annotation
				return propertyRetriever.metaData.getValueAnnotation(annotationType);
			}

			// Delegate to obtain value annoation
			return propertyRetriever.retriever.getValueAnnotation(remainingName, annotationType);
		});
	}

	/**
	 * {@link Processor} to retrieve the value.
	 */
	private static Processor valueRetriever = (propertyRetriever, object, remainingName) -> {

		// Ensure able to retrieve value
		if (propertyRetriever == null) {
			return null; // Unknown value
		}

		// Return the retrieved value
		return propertyRetriever.retriever.retrieveValue(object, remainingName);
	};

	@Override
	public Object retrieveValue(T object, String name) throws HttpException {
		// Return the retrieved value
		return this.process(name, object, valueRetriever);
	}

	/**
	 * Processes.
	 * 
	 * @param name
	 *            Property name to retrieve.
	 * @param object
	 *            Object on the value. May be null.
	 * @param processor
	 *            {@link Processor}.
	 * @return Value as per the {@link Processor}.
	 * @throws HttpException
	 *             If fails to process.
	 */
	private  R process(String name, Object object, Processor processor) throws HttpException {

		// Obtain the property name
		String propertyName;
		String remainingName;
		int splitIndex = name.indexOf('.');
		if (splitIndex < 0) {
			propertyName = name;
			remainingName = "";
		} else {
			propertyName = name.substring(0, splitIndex);
			remainingName = name.substring(splitIndex + 1); // +1 ignore '.'
		}

		// Translate the property name
		if (this.isCaseInsensitive) {
			propertyName = propertyName.toLowerCase();
		}

		// Obtain the property value retrieve struct
		RetrieveStruct retrieveStruct = this.propertyToRetriever.get(propertyName);

		// Process
		return processor.process(retrieveStruct, object, remainingName);
	}

	/**
	 * Processor.
	 */
	private interface Processor {

		/**
		 * Processes the details.
		 * 
		 * @param retrieveStruct
		 *            {@link RetrieveStruct} for the property. May be
		 *            null.
		 * @param object
		 *            Object to retrieve value from.
		 * @param remainingName
		 *            Remaining name of the property.
		 * @return Value as per processing.
		 * @throws HttpException
		 *             If fails to process.
		 */
		R process(RetrieveStruct retrieveStruct, Object object, String remainingName) throws HttpException;
	}

	/**
	 * Retrieve struct.
	 */
	private static class RetrieveStruct {

		/**
		 * {@link ValueRetriever}.
		 */
		public final ValueRetriever retriever;

		/**
		 * {@link PropertyMetaData}.
		 */
		public final PropertyMetaData metaData;

		/**
		 * Initiate.
		 * 
		 * @param retriever
		 *            {@link ValueRetriever}.
		 * @param metaData
		 *            {@link PropertyMetaData}.
		 */
		public RetrieveStruct(ValueRetriever retriever, PropertyMetaData metaData) {
			this.retriever = retriever;
			this.metaData = metaData;
		}
	}

}