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

org.usf.jquery.web.ContextEnvironment Maven / Gradle / Ivy

package org.usf.jquery.web;

import static java.lang.reflect.Modifier.isStatic;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.usf.jquery.core.Validation.requireLegalVariable;
import static org.usf.jquery.core.Validation.requireNonEmpty;
import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;

import javax.sql.DataSource;

import org.usf.jquery.core.JQueryException;
import org.usf.jquery.core.NamedColumn;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

/**
 * 
 * @author u$f
 *
 */
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class ContextEnvironment {
	
	private static final Set RESERVED_WORDS = Stream.of(Parameters.class.getDeclaredFields())
			.filter(f-> isStatic(f.getModifiers()))
			.map(Field::getName)
			.collect(toSet());
	
	private final DatabaseDecorator database;
	private final Map views;
	private final Map columns;
	private final DataSource dataSource; //optional
	private final String schema; //optional
	private final DatabaseMetadata metadata;
	
	private Map declaredColumns = new HashMap<>();
	
	ContextEnvironment(ContextEnvironment ctx) {
		this.database = ctx.database;
		this.views = new HashMap<>(ctx.views); //modifiable
		this.columns = ctx.columns;
		this.dataSource = ctx.dataSource;
		this.schema = ctx.schema;
		this.metadata = ctx.metadata;
	}
	
	public Optional lookupRegisteredView(String name) { //+ declared
		return ofNullable(views.get(name));
	}
	
	public Optional lookupRegisteredColumn(String name) {
		return ofNullable(columns.get(name));
	}

	Optional lookupDeclaredColumn(String name) {
		return ofNullable(declaredColumns.get(name));
	}

	ViewDecorator declareView(ViewDecorator view) { //additional request views
		return views.compute(view.identity(), (k,v)-> {
			if(isNull(v)){
				return view;
			}
			throw resourceAlreadyExistsException(k);
		});
	}

	NamedColumn declareColumn(NamedColumn col) {
		views.computeIfPresent(col.getTag(), (k,v)-> { //cannot overwrite registered views
			throw resourceAlreadyExistsException(k);
		}); //but can overwrite registered columns
		return declaredColumns.compute(col.getTag(), (k,v)-> {
			if(isNull(v)){
				return col;
			} //cannot overwrite declared column
			throw resourceAlreadyExistsException(k);
		});
	}
	
	ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) {
		return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values()));
	}
	
	ContextEnvironment bind() {
		if(nonNull(dataSource)) {
			try(var cnx = dataSource.getConnection()) {
				var cm = cnx.getMetaData();
				metadata.fetch(cm);
				for(var v : views.values()) {
					var meta = requireNonNull(v.metadata(), v.identity() + ".metadata");
					synchronized(meta) {
						meta.fetch(cm, schema);
					}
				}
			}
			catch (SQLException e) {
				throw new JQueryException(e);
			}
		}
		return this;
	}

	public static ContextEnvironment of(DatabaseDecorator database, 
			Collection views,  Collection columns) {
		return of(database, views, columns, null, null);
	}

	public static ContextEnvironment of(DatabaseDecorator database, 
			Collection views,  Collection columns, DataSource ds) {
		return of(database, views, columns, ds, null);
	}
	
	public static ContextEnvironment of(DatabaseDecorator database, 
			Collection views, Collection columns, DataSource ds, String schema) {
		assertIdentity(requireNonNull(database, "configuration.database").identity());
		return new ContextEnvironment(database,
				unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), //preserve views order
				unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"),
				ds, schema, new DatabaseMetadata());
	}
	
	static  Map unmodifiableIdentityMap(Collection c, Function fn, String msg){
		return unmodifiableMap(requireNonEmpty(c, msg).stream()
				.collect(toLinkedMap(fn.andThen(ContextEnvironment::assertIdentity), identity())));
	}
	
	static  Collector> toLinkedMap(
    		Function keyMapper, 
    		Function valueMapper) {
		return toMap(keyMapper, valueMapper, 
				(v1, v2) -> {throw new IllegalStateException("!parallel");},
                LinkedHashMap::new);
	}
	
	static String assertIdentity(String id) {
		if(!RESERVED_WORDS.contains(requireLegalVariable(id))){
			return id;
		}
		throw new IllegalArgumentException("reserved word cannot be used as an identifier: " + id);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy