/* * #%L * FlatPack serialization code * %% * Copyright (C) 2012 Perka Inc. * %% * 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. * #L% */ package com.getperka.flatpack.inject; import java.security.Principal; import java.util.ArrayDeque; import java.util.Deque; import java.util.Map; import org.joda.time.DateTime; import com.getperka.flatpack.FlatPackEntity; import com.getperka.flatpack.TraversalMode; import com.getperka.flatpack.util.FlatPackCollections; import com.google.gson.stream.JsonWriter; import com.google.inject.Key; import com.google.inject.OutOfScopeException; import com.google.inject.Provider; import com.google.inject.Scope; /** * Provides injection for objects whose lifespans are bounded by a pack or unpack operation. */ public class PackScope implements Scope { private static class DummyProvider implements Provider<Object> { private static final DummyProvider INSTANCE = new DummyProvider(); @Override public Object get() { throw new IllegalStateException("Not in a PackScope"); } } /** * Returns a dummy provider that always throws an exception. This provider is injected as the * default provider for bindings that can only be satisfied inside of the pack scope. */ public static <T> Provider<T> provider() { return cast(DummyProvider.INSTANCE); } /** * Utility method for an unchecked cast. */ @SuppressWarnings("unchecked") private static <T> Provider<T> cast(Provider<?> provider) { return (Provider<T>) provider; } /** * Retains the thread-local data. */ private final ThreadLocal<Deque<Map<Key<?>, Object>>> allData = new ThreadLocal<Deque<Map<Key<?>, Object>>>() { @Override protected Deque<Map<Key<?>, Object>> initialValue() { return new ArrayDeque<Map<Key<?>, Object>>(); } }; PackScope() {} public PackScope enter() { allData.get().push(FlatPackCollections.<Key<?>, Object> mapForLookup()); return this; } public void exit() { Deque<Map<Key<?>, Object>> deque = allData.get(); if (!deque.isEmpty()) { deque.pop(); } } public boolean isEntered() { return !allData.get().isEmpty(); } @Override public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) { return cast(new Provider<Object>() { @Override public Object get() { Map<Key<?>, Object> map = data(); if (map == null) { throw new OutOfScopeException("Not in a PackScope"); } Object toReturn = map.get(key); if (toReturn == null) { toReturn = unscoped.get(); map.put(key, toReturn); } return toReturn; } }); } public PackScope withEntity(FlatPackEntity<?> entity) { withLastModifiedTime(entity.getLastModifiedTime()); withPrincipal(entity.getPrincipal()); withTraversalMode(entity.getTraversalMode()); return this; } public PackScope withJsonWriter(JsonWriter writer) { Key<JsonWriter> key = Key.get(JsonWriter.class); if (writer == null) { data().remove(key); } else { data().put(key, writer); } return this; } public PackScope withLastModifiedTime(DateTime lastModified) { Key<DateTime> key = Key.get(DateTime.class, LastModifiedTime.class); if (lastModified == null) { lastModified = new DateTime(0); } data().put(key, lastModified); return this; } public PackScope withPrincipal(Principal principal) { Key<Principal> key = Key.get(Principal.class); if (principal == null) { data().remove(key); } else { data().put(key, principal); } return this; } public PackScope withTraversalMode(TraversalMode mode) { Key<TraversalMode> key = Key.get(TraversalMode.class); if (mode == null) { data().remove(key); } else { data().put(key, mode); } return this; } private Map<Key<?>, Object> data() { return allData.get().peek(); } }