From d51363340592000a352ee6979a0cf81b9b75ac27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasan=20Demirta=C5=9F?= Date: Mon, 30 Sep 2024 18:20:57 +0300 Subject: [PATCH] development. (#2) --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- README.md | 3 +- .../annotation/AnnotatedMethodHandler.java | 165 ++++++ .../frame/annotation/ParameterDescriptor.java | 16 + .../frame/annotation/ParameterValue.java | 63 +++ .../frame/annotation/ParameterValueImpl.java | 26 + .../annotation/config/ConfigKeyProvider.java | 9 + .../config/ConfigValueProvider.java | 19 + .../config/ConfigValueProviderEmpty.java | 37 ++ .../config/ConfigValueProviderMap.java | 58 +++ .../frame/annotation/decorator/Order.java | 12 + .../decorator/config/ViewCancelOnClick.java | 12 + .../decorator/config/ViewCancelOnDrag.java | 12 + .../decorator/config/ViewCancelOnDrop.java | 12 + .../decorator/config/ViewCancelOnPickup.java | 12 + .../decorator/config/ViewConfigModifier.java | 11 + .../interaction/ViewInteractionDelay.java | 12 + .../ViewInteractionDelayConfigKey.java | 14 + .../ViewInteractionDelayProvide.java | 16 + .../ViewInteractionDelayProvideConfigKey.java | 15 + .../interaction/ViewOnInteractionDelay.java | 12 + .../decorator/config/layout/ViewLayout.java | 12 + .../config/layout/ViewLayoutConfigKey.java | 14 + .../config/layout/ViewLayoutProvide.java | 16 + .../layout/ViewLayoutProvideConfigKey.java | 15 + .../decorator/config/layout/ViewOnLayout.java | 12 + .../decorator/config/size/ViewOnSize.java | 12 + .../decorator/config/size/ViewSize.java | 12 + .../config/size/ViewSizeConfigKey.java | 14 + .../config/size/ViewSizeProvide.java | 16 + .../config/size/ViewSizeProvideConfigKey.java | 15 + .../decorator/config/title/ViewOnTitle.java | 12 + .../decorator/config/title/ViewTitle.java | 12 + .../config/title/ViewTitleConfigKey.java | 14 + .../config/title/ViewTitleProvide.java | 16 + .../title/ViewTitleProvideConfigKey.java | 15 + .../ViewOnTransitiveInitialData.java | 12 + .../transitive/ViewTransitiveInitialData.java | 12 + .../ViewTransitiveInitialDataConfigKey.java | 14 + .../ViewTransitiveInitialDataProvide.java | 16 + ...TransitiveInitialDataProvideConfigKey.java | 15 + .../decorator/config/type/ViewOnType.java | 12 + .../decorator/config/type/ViewType.java | 13 + .../config/type/ViewTypeConfigKey.java | 14 + .../config/type/ViewTypeProvide.java | 17 + .../config/type/ViewTypeProvideConfigKey.java | 15 + .../config/update/ViewOnUpdateInterval.java | 12 + .../config/update/ViewUpdateInterval.java | 12 + .../update/ViewUpdateIntervalConfigKey.java | 14 + .../update/ViewUpdateIntervalProvide.java | 16 + .../ViewUpdateIntervalProvideConfigKey.java | 15 + .../decorator/element/ElementBack.java | 11 + .../element/ElementCancelOnClick.java | 11 + .../element/ElementCloseOnClick.java | 11 + .../element/ElementNavigateOnClick.java | 12 + .../element/ElementUpdateOnClick.java | 11 + .../element/itemstack/ElementConfigKey.java | 14 + .../itemstack/ElementProvideConfigKey.java | 15 + .../itemstack/ElementProvideItemStack.java | 15 + .../pagination/ElementPaginationAdvance.java | 14 + .../pagination/ElementPaginationBack.java | 14 + .../element/slot/ElementSlotConfigKey.java | 14 + .../element/slot/ElementSlotIndex.java | 12 + .../element/slot/ElementSlotLayout.java | 12 + .../element/slot/ElementSlotPosition.java | 14 + .../slot/ElementSlotProvideConfigKey.java | 15 + .../element/slot/ElementSlotProvideIndex.java | 15 + .../slot/ElementSlotProvideLayout.java | 15 + .../slot/ElementSlotProvidePosition.java | 15 + .../element/vault/ElementVaultLayout.java | 12 + .../vault/ElementVaultLayoutConfigKey.java | 14 + .../vault/ElementVaultLayoutProvide.java | 15 + .../ElementVaultLayoutProvideConfigKey.java | 15 + .../element/vault/ElementVaultLoadOnOpen.java | 11 + .../vault/ElementVaultSaveOnClose.java | 11 + .../annotation/decorator/state/StateKey.java | 12 + .../decorator/view/ViewOnClose.java | 11 + .../decorator/view/ViewOnFirstRender.java | 11 + .../decorator/view/ViewOnGlobalClick.java | 11 + .../annotation/decorator/view/ViewOnInit.java | 11 + .../annotation/decorator/view/ViewOnOpen.java | 11 + .../decorator/view/ViewOnUpdate.java | 11 + ...tatedMethodHandlerInitiationException.java | 10 + build.gradle.kts | 9 +- common/build.gradle.kts | 8 + .../main/java/net/infumia/frame/Frame.java | 112 ++++ .../java/net/infumia/frame/FrameFactory.java | 19 + .../main/java/net/infumia/frame/Internal.java | 30 ++ .../net/infumia/frame/context/Context.java | 17 + .../infumia/frame/context/ContextBase.java | 55 ++ .../context/element/ContextElementClear.java | 10 + .../context/element/ContextElementClick.java | 10 + .../element/ContextElementItemClick.java | 10 + .../element/ContextElementItemRender.java | 20 + .../element/ContextElementItemUpdate.java | 10 + .../context/element/ContextElementRender.java | 10 + .../context/element/ContextElementUpdate.java | 13 + .../frame/context/view/ContextClick.java | 49 ++ .../frame/context/view/ContextClose.java | 13 + .../frame/context/view/ContextInit.java | 17 + .../frame/context/view/ContextOpen.java | 22 + .../frame/context/view/ContextRender.java | 44 ++ .../frame/context/view/ContextResume.java | 13 + .../net/infumia/frame/element/Element.java | 24 + .../infumia/frame/element/ElementBuilder.java | 45 ++ .../frame/element/ElementContainer.java | 11 + .../infumia/frame/element/ElementItem.java | 25 + .../frame/element/ElementItemBuilder.java | 80 +++ .../element/ElementItemBuilderFactory.java | 40 ++ .../element/pagination/ElementPagination.java | 34 ++ .../pagination/ElementPaginationBuilder.java | 88 ++++ .../itemstack/ElementProviderItemStack.java | 10 + .../slot/ElementSlotProviderIndex.java | 8 + .../slot/ElementSlotProviderLayout.java | 8 + .../slot/ElementSlotProviderPosition.java | 10 + .../vault/ElementVaultLayoutProvider.java | 8 + .../frame/extension/PipelineExtensions.java | 18 + .../frame/injection/AnnotationAccessor.java | 30 ++ .../AnnotationAccessorAnnotated.java | 31 ++ .../injection/AnnotationAccessorEmpty.java | 26 + .../AnnotationAccessorMultiDelegate.java | 41 ++ .../frame/injection/InjectionRequester.java | 27 + .../injection/InjectionRequesterImpl.java | 55 ++ .../frame/injection/InjectionService.java | 14 + .../injection/InjectionServiceInjector.java | 38 ++ .../injection/InjectionServicePipeline.java | 29 ++ .../InjectionServicePipelineImpl.java | 36 ++ .../frame/injector/InjectionRequest.java | 31 ++ .../frame/injector/InjectionRequestImpl.java | 45 ++ .../net/infumia/frame/injector/Injector.java | 10 + .../frame/injector/InjectorRegistry.java | 31 ++ .../frame/injector/InjectorRegistryImpl.java | 67 +++ .../frame/injector/guice/InjectorGuice.java | 40 ++ .../frame/injector/guice/KeyCreator.java | 21 + .../guice/KeyCreatorFirstBinding.java | 28 + .../injector/guice/KeyCreatorNoBinding.java | 21 + .../java/net/infumia/frame/logger/Logger.java | 23 + .../frame/metadata/CacheKeyExtractor.java | 7 + .../frame/metadata/MetadataAccess.java | 32 ++ .../frame/metadata/MetadataAccessFactory.java | 12 + .../net/infumia/frame/pipeline/Pipeline.java | 4 + .../infumia/frame/pipeline/PipelineBase.java | 85 +++ .../frame/pipeline/PipelineConsumer.java | 6 + .../frame/pipeline/PipelineContext.java | 3 + .../frame/pipeline/PipelineService.java | 5 + .../pipeline/PipelineServiceConsumer.java | 6 + .../net/infumia/frame/pipeline/Pipelined.java | 8 + .../context/PipelineContextElement.java | 31 ++ .../context/PipelineContextManager.java | 29 ++ .../context/PipelineContextRender.java | 45 ++ .../context/PipelineContextState.java | 29 ++ .../pipeline/context/PipelineContextView.java | 123 +++++ .../context/PipelineContextViewer.java | 22 + .../executor/PipelineExecutorElement.java | 42 ++ .../executor/PipelineExecutorManager.java | 57 ++ .../executor/PipelineExecutorRender.java | 82 +++ .../executor/PipelineExecutorState.java | 33 ++ .../executor/PipelineExecutorView.java | 146 ++++++ .../executor/PipelineExecutorViewer.java | 25 + .../infumia/frame/service/Cancellable.java | 7 + .../frame/service/ConsumerService.java | 42 ++ .../infumia/frame/service/Implementation.java | 77 +++ .../net/infumia/frame/service/Register.java | 27 + .../infumia/frame/service/RegisterAfter.java | 58 +++ .../infumia/frame/service/RegisterBefore.java | 48 ++ .../net/infumia/frame/service/Replace.java | 60 +++ .../net/infumia/frame/service/Service.java | 10 + .../frame/service/ServicePipeline.java | 32 ++ .../frame/service/ServicePipelineBuilder.java | 44 ++ .../frame/service/ServiceRepository.java | 53 ++ .../infumia/frame/service/ServiceSpigot.java | 145 ++++++ .../infumia/frame/service/ServiceWrapper.java | 65 +++ .../service/exception/PipelineException.java | 10 + .../net/infumia/frame/slot/LayoutSlot.java | 19 + .../java/net/infumia/frame/state/State.java | 26 + .../net/infumia/frame/state/StateFactory.java | 129 +++++ .../net/infumia/frame/state/StateInitial.java | 5 + .../net/infumia/frame/state/StateMutable.java | 18 + .../state/pagination/ElementConfigurer.java | 16 + .../state/pagination/StatePagination.java | 6 + .../frame/state/value/StateUpdate.java | 42 ++ .../infumia/frame/state/value/StateValue.java | 12 + .../frame/state/value/StateValueHost.java | 3 + .../state/value/StateValueHostHolder.java | 8 + .../state/watcher/StateWatcherAccess.java | 9 + .../state/watcher/StateWatcherUpdate.java | 9 + .../net/infumia/frame/task/TaskFactory.java | 18 + .../java/net/infumia/frame/type/InvType.java | 23 + .../net/infumia/frame/typedkey/TypedKey.java | 58 +++ .../frame/typedkey/TypedKeyStorage.java | 21 + .../typedkey/TypedKeyStorageFactory.java | 23 + .../typedkey/TypedKeyStorageFactoryImpl.java | 36 ++ .../typedkey/TypedKeyStorageImmutable.java | 29 ++ .../TypedKeyStorageImmutableBuilder.java | 14 + .../TypedKeyStorageImmutableBuilderImpl.java | 38 ++ .../TypedKeyStorageImmutableEmpty.java | 58 +++ .../TypedKeyStorageImmutableImpl.java | 63 +++ .../frame/typedkey/TypedKeyStorageImpl.java | 67 +++ .../java/net/infumia/frame/util/Cache.java | 49 ++ .../java/net/infumia/frame/util/Keyed.java | 5 + .../java/net/infumia/frame/util/Lazy.java | 24 + .../java/net/infumia/frame/util/Pair.java | 45 ++ .../java/net/infumia/frame/util/PaperLib.java | 98 ++++ .../net/infumia/frame/util/Preconditions.java | 56 ++ .../net/infumia/frame/util/Reflection.java | 65 +++ .../infumia/frame/util/RunnableThrowable.java | 6 + .../java/net/infumia/frame/util/Ticks.java | 15 + .../java/net/infumia/frame/view/View.java | 14 + .../net/infumia/frame/view/ViewContainer.java | 41 ++ .../net/infumia/frame/view/ViewCreator.java | 17 + .../net/infumia/frame/view/ViewHandler.java | 30 ++ .../infumia/frame/view/config/ViewConfig.java | 37 ++ .../frame/view/config/ViewConfigBuilder.java | 54 ++ .../frame/view/config/ViewConfigModifier.java | 7 + .../view/config/option/ViewConfigOption.java | 23 + .../option/ViewConfigOptionController.java | 12 + .../config/option/ViewConfigOptionImpl.java | 56 ++ .../view/config/option/ViewConfigOptions.java | 18 + .../frame/view/creator/InventoryCreator.java | 17 + .../frame/viewer/ContextualViewer.java | 9 + .../java/net/infumia/frame/viewer/Viewer.java | 22 + .../infumia/frame/viewer/ViewerCreator.java | 10 + core/build.gradle.kts | 12 + .../net/infumia/frame/FrameFactoryImpl.java | 41 ++ .../java/net/infumia/frame/FrameImpl.java | 308 +++++++++++ .../java/net/infumia/frame/FrameRich.java | 9 + .../java/net/infumia/frame/InvTypeRich.java | 144 +++++ .../main/java/net/infumia/frame/InvTypes.java | 112 ++++ .../java/net/infumia/frame/SlotConverter.java | 31 ++ .../frame/config/ViewConfigBuilderImpl.java | 225 ++++++++ .../frame/config/ViewConfigBuilderRich.java | 14 + .../infumia/frame/config/ViewConfigImpl.java | 111 ++++ .../infumia/frame/config/ViewConfigRich.java | 14 + .../frame/context/ContextBaseImpl.java | 199 +++++++ .../frame/context/ContextBaseRich.java | 13 + .../infumia/frame/context/ContextImpl.java | 55 ++ .../infumia/frame/context/ContextRich.java | 9 + .../element/ContextElementClearImpl.java | 27 + .../element/ContextElementClickImpl.java | 29 ++ .../element/ContextElementItemClickImpl.java | 25 + .../element/ContextElementItemRenderImpl.java | 51 ++ .../element/ContextElementItemUpdateImpl.java | 25 + .../element/ContextElementRenderImpl.java | 29 ++ .../element/ContextElementUpdateImpl.java | 49 ++ .../frame/context/view/ContextClickImpl.java | 129 +++++ .../frame/context/view/ContextCloseImpl.java | 38 ++ .../frame/context/view/ContextInitImpl.java | 39 ++ .../frame/context/view/ContextOpenImpl.java | 61 +++ .../frame/context/view/ContextRenderImpl.java | 333 ++++++++++++ .../frame/context/view/ContextRenderRich.java | 36 ++ .../frame/context/view/ContextResumeImpl.java | 33 ++ .../frame/element/ElementBuilderImpl.java | 139 +++++ .../frame/element/ElementBuilderRich.java | 12 + .../frame/element/ElementEventHandler.java | 24 + .../element/ElementEventHandlerHolder.java | 8 + .../element/ElementEventHandlerItem.java | 172 ++++++ .../infumia/frame/element/ElementImpl.java | 129 +++++ .../frame/element/ElementItemBuilderImpl.java | 198 +++++++ .../frame/element/ElementItemBuilderRich.java | 12 + .../frame/element/ElementItemImpl.java | 96 ++++ .../frame/element/ElementItemRich.java | 9 + .../infumia/frame/element/ElementRich.java | 30 ++ .../ElementEventHandlerPagination.java | 150 ++++++ .../ElementPaginationBuilderImpl.java | 197 +++++++ .../ElementPaginationBuilderRich.java | 16 + .../pagination/ElementPaginationImpl.java | 491 ++++++++++++++++++ .../pagination/ElementPaginationRich.java | 38 ++ .../element/pagination/SourceProvider.java | 92 ++++ .../CompletableFutureExtensions.java | 29 ++ .../frame/listener/InventoryListener.java | 125 +++++ .../infumia/frame/logger/PluginLogger.java | 75 +++ .../CacheKeyExtractorEntityUniqueId.java | 19 + .../metadata/MetadataAccessFactoryImpl.java | 46 ++ .../frame/metadata/MetadataAccessImpl.java | 96 ++++ .../frame/metadata/MetadataKeyHolder.java | 32 ++ .../frame/pipeline/PipelineConsumerImpl.java | 42 ++ .../infumia/frame/pipeline/PipelineImpl.java | 39 ++ .../context/PipelineContextElements.java | 93 ++++ .../context/PipelineContextManagers.java | 101 ++++ .../context/PipelineContextRenders.java | 164 ++++++ .../context/PipelineContextStates.java | 88 ++++ .../context/PipelineContextViewers.java | 60 +++ .../context/PipelineContextViews.java | 372 +++++++++++++ .../executor/PipelineExecutorElementImpl.java | 119 +++++ .../executor/PipelineExecutorManagerImpl.java | 112 ++++ .../executor/PipelineExecutorRenderImpl.java | 155 ++++++ .../executor/PipelineExecutorStateImpl.java | 66 +++ .../executor/PipelineExecutorViewImpl.java | 271 ++++++++++ .../executor/PipelineExecutorViewerImpl.java | 60 +++ .../holder/PipelineHolderElement.java | 87 ++++ .../holder/PipelineHolderManager.java | 96 ++++ .../pipeline/holder/PipelineHolderRender.java | 151 ++++++ .../pipeline/holder/PipelineHolderState.java | 50 ++ .../pipeline/holder/PipelineHolderView.java | 227 ++++++++ .../pipeline/holder/PipelineHolderViewer.java | 64 +++ .../service/element/ServiceClear.java | 34 ++ .../service/element/ServiceClearLogging.java | 31 ++ .../service/element/ServiceClick.java | 34 ++ .../element/ServiceClickCancelOnClick.java | 30 ++ .../element/ServiceClickCloseOnClick.java | 30 ++ .../service/element/ServiceClickLogging.java | 31 ++ .../element/ServiceClickUpdateOnClick.java | 36 ++ .../service/element/ServiceRender.java | 33 ++ .../service/element/ServiceRenderLogging.java | 31 ++ .../service/element/ServiceUpdate.java | 33 ++ .../service/element/ServiceUpdateLogging.java | 31 ++ .../manager/ServiceListenerRegistered.java | 28 + .../ServiceListenerRegisteredLogging.java | 27 + .../service/manager/ServiceViewCreated.java | 47 ++ .../manager/ServiceViewRegistered.java | 66 +++ .../ServiceViewUnregisteredLogging.java | 26 + .../service/render/ServiceFirstRender.java | 44 ++ ...iceFirstRenderAvailableSlotResolution.java | 122 +++++ ...eFirstRenderConsumeNonRenderedElement.java | 37 ++ .../ServiceFirstRenderInitializeState.java | 39 ++ .../render/ServiceFirstRenderLayout.java | 45 ++ .../render/ServiceFirstRenderLogging.java | 33 ++ .../ServiceFirstRenderOnFirstRender.java | 30 ++ .../render/ServiceFirstRenderPagination.java | 43 ++ .../render/ServiceFirstRenderWatchState.java | 91 ++++ .../service/render/ServiceOpenContainer.java | 35 ++ .../render/ServiceOpenContainerLogging.java | 27 + .../service/render/ServiceResumeLogging.java | 36 ++ .../service/render/ServiceResumeOnResume.java | 32 ++ .../service/render/ServiceStartUpdate.java | 52 ++ .../render/ServiceStartUpdateCancel.java | 38 ++ .../render/ServiceStartUpdateInvalidate.java | 47 ++ .../render/ServiceStartUpdateLogging.java | 31 ++ .../service/render/ServiceStopUpdate.java | 35 ++ .../render/ServiceStopUpdateLogging.java | 31 ++ .../service/render/ServiceUpdateLogging.java | 31 ++ .../service/render/ServiceUpdateOnUpdate.java | 32 ++ .../service/state/ServiceAccessLogging.java | 34 ++ .../service/state/ServiceUpdateLogging.java | 36 ++ .../service/view/ServiceClickCancel.java | 32 ++ .../service/view/ServiceClickElement.java | 45 ++ .../view/ServiceClickInteractionDelay.java | 62 +++ .../service/view/ServiceClickLogging.java | 36 ++ .../service/view/ServiceClickOnClick.java | 32 ++ .../pipeline/service/view/ServiceClose.java | 30 ++ .../service/view/ServiceCloseCancel.java | 46 ++ .../service/view/ServiceCloseLogging.java | 35 ++ .../service/view/ServiceCloseOnClose.java | 30 ++ .../service/view/ServiceCreateContainer.java | 81 +++ .../service/view/ServiceCreateContext.java | 51 ++ .../service/view/ServiceCreateRender.java | 34 ++ .../service/view/ServiceCreateViewers.java | 45 ++ .../service/view/ServiceInitLogging.java | 30 ++ .../service/view/ServiceInitOnInit.java | 29 ++ .../service/view/ServiceInitWaitUntil.java | 41 ++ .../service/view/ServiceLayoutResolution.java | 67 +++ .../view/ServiceLayoutResolutionLogging.java | 26 + .../view/ServiceModifyContainerLogging.java | 26 + .../view/ServiceOpenInitializeState.java | 33 ++ .../service/view/ServiceOpenLogging.java | 30 ++ .../service/view/ServiceOpenOnOpen.java | 29 ++ .../service/view/ServiceOpenPreviousView.java | 45 ++ .../service/view/ServiceOpenWaitUntil.java | 41 ++ .../view/ServiceProcessConfigModifier.java | 34 ++ ...eProcessConfigModifierAddSizeModifier.java | 57 ++ .../ServiceProcessConfigModifierLogging.java | 27 + .../service/view/ServiceTransition.java | 36 ++ .../view/ServiceTransitionLogging.java | 48 ++ .../pipeline/service/viewer/ServiceAdded.java | 30 ++ .../viewer/ServiceAddedContextualViewer.java | 38 ++ .../service/viewer/ServiceAddedLogging.java | 34 ++ .../viewer/ServiceAddedOnViewerAdded.java | 37 ++ .../service/viewer/ServiceRemoved.java | 31 ++ .../ServiceRemovedContextualViewer.java | 39 ++ .../service/viewer/ServiceRemovedLogging.java | 34 ++ .../viewer/ServiceRemovedOnViewerRemoved.java | 37 ++ .../viewer/ServiceRemovedStopUpdateTask.java | 33 ++ .../infumia/frame/slot/LayoutSlotImpl.java | 45 ++ .../net/infumia/frame/slot/SlotFinder.java | 82 +++ .../infumia/frame/state/StateFactoryImpl.java | 338 ++++++++++++ .../net/infumia/frame/state/StateImpl.java | 115 ++++ .../infumia/frame/state/StateInitialImpl.java | 23 + .../infumia/frame/state/StateInitialRich.java | 3 + .../infumia/frame/state/StateMutableImpl.java | 31 ++ .../infumia/frame/state/StateMutableRich.java | 3 + .../frame/state/StatePaginationImpl.java | 17 + .../frame/state/StatePaginationRich.java | 6 + .../infumia/frame/state/StateRegistry.java | 64 +++ .../net/infumia/frame/state/StateRich.java | 21 + .../frame/state/value/StateValueComputed.java | 30 ++ .../frame/state/value/StateValueFactory.java | 8 + .../frame/state/value/StateValueHostImpl.java | 236 +++++++++ .../frame/state/value/StateValueHostRich.java | 58 +++ .../state/value/StateValueImmutable.java | 29 ++ .../frame/state/value/StateValueInitial.java | 71 +++ .../frame/state/value/StateValueMutable.java | 28 + .../infumia/frame/task/TaskFactoryImpl.java | 60 +++ .../infumia/frame/view/ViewContainerImpl.java | 108 ++++ .../infumia/frame/view/ViewContainerRich.java | 9 + .../infumia/frame/view/ViewCreatorImpl.java | 82 +++ .../infumia/frame/view/ViewEventHandler.java | 47 ++ .../java/net/infumia/frame/view/ViewImpl.java | 214 ++++++++ .../view/creator/InventoryCreatorBukkit.java | 40 ++ .../view/creator/InventoryCreatorPaper.java | 42 ++ .../frame/viewer/ContextualViewerImpl.java | 23 + .../frame/viewer/ViewerCreatorImpl.java | 21 + .../net/infumia/frame/viewer/ViewerImpl.java | 77 +++ gradle/libs.versions.toml | 6 + settings.gradle.kts | 2 +- 405 files changed, 17683 insertions(+), 5 deletions(-) create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/AnnotatedMethodHandler.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/ParameterDescriptor.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/ParameterValue.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/ParameterValueImpl.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/config/ConfigKeyProvider.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProvider.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderEmpty.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderMap.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/Order.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrag.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrop.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnPickup.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewConfigModifier.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelay.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewOnInteractionDelay.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayout.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewOnLayout.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewOnSize.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSize.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewOnTitle.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitle.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewOnTransitiveInitialData.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialData.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewOnType.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewType.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewOnUpdateInterval.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateInterval.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementBack.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCancelOnClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCloseOnClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementNavigateOnClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementUpdateOnClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideItemStack.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationAdvance.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationBack.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotIndex.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotLayout.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotPosition.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideIndex.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideLayout.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvidePosition.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayout.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvide.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvideConfigKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLoadOnOpen.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultSaveOnClose.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/state/StateKey.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnClose.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnFirstRender.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnGlobalClick.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnInit.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnOpen.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnUpdate.java create mode 100644 annotation/src/main/java/net/infumia/frame/annotation/exception/AnnotatedMethodHandlerInitiationException.java create mode 100644 common/src/main/java/net/infumia/frame/Frame.java create mode 100644 common/src/main/java/net/infumia/frame/FrameFactory.java create mode 100644 common/src/main/java/net/infumia/frame/Internal.java create mode 100644 common/src/main/java/net/infumia/frame/context/Context.java create mode 100644 common/src/main/java/net/infumia/frame/context/ContextBase.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementClear.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementClick.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementItemClick.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementItemRender.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdate.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementRender.java create mode 100644 common/src/main/java/net/infumia/frame/context/element/ContextElementUpdate.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextClick.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextClose.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextInit.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextOpen.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextRender.java create mode 100644 common/src/main/java/net/infumia/frame/context/view/ContextResume.java create mode 100644 common/src/main/java/net/infumia/frame/element/Element.java create mode 100644 common/src/main/java/net/infumia/frame/element/ElementBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/element/ElementContainer.java create mode 100644 common/src/main/java/net/infumia/frame/element/ElementItem.java create mode 100644 common/src/main/java/net/infumia/frame/element/ElementItemBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/element/ElementItemBuilderFactory.java create mode 100644 common/src/main/java/net/infumia/frame/element/pagination/ElementPagination.java create mode 100644 common/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/element/provider/itemstack/ElementProviderItemStack.java create mode 100644 common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderIndex.java create mode 100644 common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderLayout.java create mode 100644 common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderPosition.java create mode 100644 common/src/main/java/net/infumia/frame/element/provider/vault/ElementVaultLayoutProvider.java create mode 100644 common/src/main/java/net/infumia/frame/extension/PipelineExtensions.java create mode 100644 common/src/main/java/net/infumia/frame/injection/AnnotationAccessor.java create mode 100644 common/src/main/java/net/infumia/frame/injection/AnnotationAccessorAnnotated.java create mode 100644 common/src/main/java/net/infumia/frame/injection/AnnotationAccessorEmpty.java create mode 100644 common/src/main/java/net/infumia/frame/injection/AnnotationAccessorMultiDelegate.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionRequester.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionRequesterImpl.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionService.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionServiceInjector.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionServicePipeline.java create mode 100644 common/src/main/java/net/infumia/frame/injection/InjectionServicePipelineImpl.java create mode 100644 common/src/main/java/net/infumia/frame/injector/InjectionRequest.java create mode 100644 common/src/main/java/net/infumia/frame/injector/InjectionRequestImpl.java create mode 100644 common/src/main/java/net/infumia/frame/injector/Injector.java create mode 100644 common/src/main/java/net/infumia/frame/injector/InjectorRegistry.java create mode 100644 common/src/main/java/net/infumia/frame/injector/InjectorRegistryImpl.java create mode 100644 common/src/main/java/net/infumia/frame/injector/guice/InjectorGuice.java create mode 100644 common/src/main/java/net/infumia/frame/injector/guice/KeyCreator.java create mode 100644 common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorFirstBinding.java create mode 100644 common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorNoBinding.java create mode 100644 common/src/main/java/net/infumia/frame/logger/Logger.java create mode 100644 common/src/main/java/net/infumia/frame/metadata/CacheKeyExtractor.java create mode 100644 common/src/main/java/net/infumia/frame/metadata/MetadataAccess.java create mode 100644 common/src/main/java/net/infumia/frame/metadata/MetadataAccessFactory.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/Pipeline.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/PipelineBase.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/PipelineConsumer.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/PipelineContext.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/PipelineService.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/PipelineServiceConsumer.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/Pipelined.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElement.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManager.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRender.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextState.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextView.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewer.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElement.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManager.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRender.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorState.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorView.java create mode 100644 common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewer.java create mode 100644 common/src/main/java/net/infumia/frame/service/Cancellable.java create mode 100644 common/src/main/java/net/infumia/frame/service/ConsumerService.java create mode 100644 common/src/main/java/net/infumia/frame/service/Implementation.java create mode 100644 common/src/main/java/net/infumia/frame/service/Register.java create mode 100644 common/src/main/java/net/infumia/frame/service/RegisterAfter.java create mode 100644 common/src/main/java/net/infumia/frame/service/RegisterBefore.java create mode 100644 common/src/main/java/net/infumia/frame/service/Replace.java create mode 100644 common/src/main/java/net/infumia/frame/service/Service.java create mode 100644 common/src/main/java/net/infumia/frame/service/ServicePipeline.java create mode 100644 common/src/main/java/net/infumia/frame/service/ServicePipelineBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/service/ServiceRepository.java create mode 100644 common/src/main/java/net/infumia/frame/service/ServiceSpigot.java create mode 100644 common/src/main/java/net/infumia/frame/service/ServiceWrapper.java create mode 100644 common/src/main/java/net/infumia/frame/service/exception/PipelineException.java create mode 100644 common/src/main/java/net/infumia/frame/slot/LayoutSlot.java create mode 100644 common/src/main/java/net/infumia/frame/state/State.java create mode 100644 common/src/main/java/net/infumia/frame/state/StateFactory.java create mode 100644 common/src/main/java/net/infumia/frame/state/StateInitial.java create mode 100644 common/src/main/java/net/infumia/frame/state/StateMutable.java create mode 100644 common/src/main/java/net/infumia/frame/state/pagination/ElementConfigurer.java create mode 100644 common/src/main/java/net/infumia/frame/state/pagination/StatePagination.java create mode 100644 common/src/main/java/net/infumia/frame/state/value/StateUpdate.java create mode 100644 common/src/main/java/net/infumia/frame/state/value/StateValue.java create mode 100644 common/src/main/java/net/infumia/frame/state/value/StateValueHost.java create mode 100644 common/src/main/java/net/infumia/frame/state/value/StateValueHostHolder.java create mode 100644 common/src/main/java/net/infumia/frame/state/watcher/StateWatcherAccess.java create mode 100644 common/src/main/java/net/infumia/frame/state/watcher/StateWatcherUpdate.java create mode 100644 common/src/main/java/net/infumia/frame/task/TaskFactory.java create mode 100644 common/src/main/java/net/infumia/frame/type/InvType.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKey.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorage.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactory.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactoryImpl.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutable.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilderImpl.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableEmpty.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableImpl.java create mode 100644 common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImpl.java create mode 100644 common/src/main/java/net/infumia/frame/util/Cache.java create mode 100644 common/src/main/java/net/infumia/frame/util/Keyed.java create mode 100644 common/src/main/java/net/infumia/frame/util/Lazy.java create mode 100644 common/src/main/java/net/infumia/frame/util/Pair.java create mode 100644 common/src/main/java/net/infumia/frame/util/PaperLib.java create mode 100644 common/src/main/java/net/infumia/frame/util/Preconditions.java create mode 100644 common/src/main/java/net/infumia/frame/util/Reflection.java create mode 100644 common/src/main/java/net/infumia/frame/util/RunnableThrowable.java create mode 100644 common/src/main/java/net/infumia/frame/util/Ticks.java create mode 100644 common/src/main/java/net/infumia/frame/view/View.java create mode 100644 common/src/main/java/net/infumia/frame/view/ViewContainer.java create mode 100644 common/src/main/java/net/infumia/frame/view/ViewCreator.java create mode 100644 common/src/main/java/net/infumia/frame/view/ViewHandler.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/ViewConfig.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/ViewConfigBuilder.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/ViewConfigModifier.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOption.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionController.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionImpl.java create mode 100644 common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptions.java create mode 100644 common/src/main/java/net/infumia/frame/view/creator/InventoryCreator.java create mode 100644 common/src/main/java/net/infumia/frame/viewer/ContextualViewer.java create mode 100644 common/src/main/java/net/infumia/frame/viewer/Viewer.java create mode 100644 common/src/main/java/net/infumia/frame/viewer/ViewerCreator.java create mode 100644 core/build.gradle.kts create mode 100644 core/src/main/java/net/infumia/frame/FrameFactoryImpl.java create mode 100644 core/src/main/java/net/infumia/frame/FrameImpl.java create mode 100644 core/src/main/java/net/infumia/frame/FrameRich.java create mode 100644 core/src/main/java/net/infumia/frame/InvTypeRich.java create mode 100644 core/src/main/java/net/infumia/frame/InvTypes.java create mode 100644 core/src/main/java/net/infumia/frame/SlotConverter.java create mode 100644 core/src/main/java/net/infumia/frame/config/ViewConfigBuilderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/config/ViewConfigBuilderRich.java create mode 100644 core/src/main/java/net/infumia/frame/config/ViewConfigImpl.java create mode 100644 core/src/main/java/net/infumia/frame/config/ViewConfigRich.java create mode 100644 core/src/main/java/net/infumia/frame/context/ContextBaseImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/ContextBaseRich.java create mode 100644 core/src/main/java/net/infumia/frame/context/ContextImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/ContextRich.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementClearImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementClickImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementItemClickImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementItemRenderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdateImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementRenderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/element/ContextElementUpdateImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextClickImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextCloseImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextInitImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextOpenImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextRenderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextRenderRich.java create mode 100644 core/src/main/java/net/infumia/frame/context/view/ContextResumeImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementBuilderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementBuilderRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementEventHandler.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementEventHandlerHolder.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementEventHandlerItem.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementItemBuilderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementItemBuilderRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementItemImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementItemRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/ElementRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/ElementEventHandlerPagination.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationImpl.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationRich.java create mode 100644 core/src/main/java/net/infumia/frame/element/pagination/SourceProvider.java create mode 100644 core/src/main/java/net/infumia/frame/extension/CompletableFutureExtensions.java create mode 100644 core/src/main/java/net/infumia/frame/listener/InventoryListener.java create mode 100644 core/src/main/java/net/infumia/frame/logger/PluginLogger.java create mode 100644 core/src/main/java/net/infumia/frame/metadata/CacheKeyExtractorEntityUniqueId.java create mode 100644 core/src/main/java/net/infumia/frame/metadata/MetadataAccessFactoryImpl.java create mode 100644 core/src/main/java/net/infumia/frame/metadata/MetadataAccessImpl.java create mode 100644 core/src/main/java/net/infumia/frame/metadata/MetadataKeyHolder.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/PipelineConsumerImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/PipelineImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElements.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManagers.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRenders.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextStates.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewers.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViews.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElementImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManagerImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRenderImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorStateImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewerImpl.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderElement.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderManager.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderRender.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderState.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderView.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderViewer.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClear.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClearLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClick.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCancelOnClick.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCloseOnClick.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickUpdateOnClick.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRender.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRenderLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdate.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdateLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegistered.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegisteredLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewCreated.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewRegistered.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewUnregisteredLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRender.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderAvailableSlotResolution.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderConsumeNonRenderedElement.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderInitializeState.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLayout.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderOnFirstRender.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderPagination.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderWatchState.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainer.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainerLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeOnResume.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdate.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateCancel.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateInvalidate.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdate.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdateLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateOnUpdate.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceAccessLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceUpdateLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickCancel.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickElement.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickInteractionDelay.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickOnClick.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClose.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseCancel.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseOnClose.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContainer.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContext.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateRender.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateViewers.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitOnInit.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitWaitUntil.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolution.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolutionLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceModifyContainerLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenInitializeState.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenOnOpen.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenPreviousView.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenWaitUntil.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifier.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierAddSizeModifier.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransition.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransitionLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAdded.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedContextualViewer.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedOnViewerAdded.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemoved.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedContextualViewer.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedLogging.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedOnViewerRemoved.java create mode 100644 core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedStopUpdateTask.java create mode 100644 core/src/main/java/net/infumia/frame/slot/LayoutSlotImpl.java create mode 100644 core/src/main/java/net/infumia/frame/slot/SlotFinder.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateFactoryImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateInitialImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateInitialRich.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateMutableImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateMutableRich.java create mode 100644 core/src/main/java/net/infumia/frame/state/StatePaginationImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/StatePaginationRich.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateRegistry.java create mode 100644 core/src/main/java/net/infumia/frame/state/StateRich.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueComputed.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueFactory.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueHostImpl.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueHostRich.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueImmutable.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueInitial.java create mode 100644 core/src/main/java/net/infumia/frame/state/value/StateValueMutable.java create mode 100644 core/src/main/java/net/infumia/frame/task/TaskFactoryImpl.java create mode 100644 core/src/main/java/net/infumia/frame/view/ViewContainerImpl.java create mode 100644 core/src/main/java/net/infumia/frame/view/ViewContainerRich.java create mode 100644 core/src/main/java/net/infumia/frame/view/ViewCreatorImpl.java create mode 100644 core/src/main/java/net/infumia/frame/view/ViewEventHandler.java create mode 100644 core/src/main/java/net/infumia/frame/view/ViewImpl.java create mode 100644 core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorBukkit.java create mode 100644 core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorPaper.java create mode 100644 core/src/main/java/net/infumia/frame/viewer/ContextualViewerImpl.java create mode 100644 core/src/main/java/net/infumia/frame/viewer/ViewerCreatorImpl.java create mode 100644 core/src/main/java/net/infumia/frame/viewer/ViewerImpl.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3718562..d36f269 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + key: "${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}" restore-keys: | ${{ runner.os }}-gradle- - run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70f119e..578699a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + key: "${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}" restore-keys: | ${{ runner.os }}-gradle- - run: | diff --git a/README.md b/README.md index b55dd35..113f3c7 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,9 @@ repositories { } dependencies { - // Base module + // Base modules implementation "net.infumia:frame:VERSION" + implementation "net.infumia:frame-core:VERSION" } ``` ### Code diff --git a/annotation/src/main/java/net/infumia/frame/annotation/AnnotatedMethodHandler.java b/annotation/src/main/java/net/infumia/frame/annotation/AnnotatedMethodHandler.java new file mode 100644 index 0000000..f2e35dd --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/AnnotatedMethodHandler.java @@ -0,0 +1,165 @@ +package net.infumia.frame.annotation; + +import io.leangen.geantyref.TypeToken; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import net.infumia.frame.annotation.exception.AnnotatedMethodHandlerInitiationException; +import net.infumia.frame.api.injection.InjectionRequester; +import net.infumia.frame.api.util.Preconditions; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Abstract class that handles annotated methods for dependency injection. + * + * @param the type of context used for method invocation. + */ +public abstract class AnnotatedMethodHandler { + + /** + * Parameters of the method being handled. + */ + @NotNull + protected final List parameters; + + /** + * MethodHandle for invoking the method. + */ + @NotNull + protected final MethodHandle methodHandle; + + /** + * Accessor for retrieving annotations from the method. + */ + @NotNull + protected final AnnotationAccessor annotationAccessor; + + /** + * Injector for managing dependencies. + */ + @NotNull + protected final InjectionRequester injector; + + /** + * Ctor. + * + * @param method the method to be handled. + * @param instance the instance on which the method will be invoked. + * @param injector the injector used to provide dependencies. + * @throws AnnotatedMethodHandlerInitiationException if the method cannot be accessed or unreflect. + */ + protected AnnotatedMethodHandler( + @NotNull final Method method, + @NotNull final Object instance, + @NotNull final InjectionRequester injector + ) { + try { + this.parameters = Arrays.asList(method.getParameters()); + if (!method.isAccessible()) { + method.setAccessible(true); + } + this.methodHandle = MethodHandles.lookup().unreflect(method).bindTo(instance); + this.annotationAccessor = AnnotationAccessor.of(method); + this.injector = injector; + } catch (final Exception exception) { + throw new AnnotatedMethodHandlerInitiationException(exception); + } + } + + /** + * Retrieves the value for a specific parameter in the given context. + *

+ * Returning {@code null} skip the contextual parameters values, instead {@link #injector} will be used. + * + * @param context the context in which the method is being invoked. + * @param parameter the parameter whose value is to be retrieved. + * @return the value of the parameter. + */ + @Nullable + @ApiStatus.OverrideOnly + protected ParameterValue getParameterValue( + @NotNull final C context, + @NotNull final Parameter parameter + ) { + return null; + } + + /** + * Creates a list of parameter values based on the current context. + * + * @param context the context in which the method is being invoked. + * @return an unmodifiable list of parameter values. + */ + @NotNull + @SuppressWarnings("unchecked") + protected CompletableFuture> createParameterValues( + @NotNull final C context + ) { + final CompletableFuture<@Nullable ParameterValue>[] futures = + this.parameters.stream() + .map(parameter -> this.createParameterValue(context, parameter)) + .toArray(CompletableFuture[]::new); + + return CompletableFuture.allOf(futures).thenApply(__ -> + Arrays.stream(futures) + .map(CompletableFuture::join) + .collect( + Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList) + ) + ); + } + + @NotNull + private CompletableFuture<@Nullable ParameterValue> createParameterValue( + @NotNull final C context, + @NotNull final Parameter parameter + ) { + final ParameterValue contextualValue = this.getParameterValue(context, parameter); + if (contextualValue != null) { + return CompletableFuture.completedFuture(contextualValue); + } + return this.requestParameterValue(parameter, context); + } + + @NotNull + private CompletableFuture requestParameterValue( + @NotNull final Parameter parameter, + @NotNull final C context + ) { + return this.requestInjection(parameter, context).thenApply(value -> { + Preconditions.argumentNotNull( + value, + "Could not create value for parameter '%s' of type '%s' in method '%s'", + parameter.getName(), + parameter.getType().getTypeName(), + this.methodHandle + ); + if (parameter.getType() == String.class) { + return ParameterValue.of(parameter, parameter.getName()); + } + return ParameterValue.of(parameter, value); + }); + } + + @NotNull + @SuppressWarnings("unchecked") + private CompletableFuture<@Nullable Object> requestInjection( + @NotNull final Parameter parameter, + @NotNull final C context + ) { + return this.injector.request( + (TypeToken) TypeToken.get(parameter.getParameterizedType()), + context, + AnnotationAccessor.of(AnnotationAccessor.of(parameter), this.annotationAccessor) + ); + } +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/ParameterDescriptor.java b/annotation/src/main/java/net/infumia/frame/annotation/ParameterDescriptor.java new file mode 100644 index 0000000..330fc77 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/ParameterDescriptor.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a descriptor for a parameter. + */ +public interface ParameterDescriptor { + /** + * Retrieves the name of the parameter. + * + * @return the name of the parameter. + */ + @NotNull + String name(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/ParameterValue.java b/annotation/src/main/java/net/infumia/frame/annotation/ParameterValue.java new file mode 100644 index 0000000..4f8809d --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/ParameterValue.java @@ -0,0 +1,63 @@ +package net.infumia.frame.annotation; + +import java.lang.reflect.Parameter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a value associated with a parameter. + */ +public interface ParameterValue { + /** + * Creates a new instance of {@link ParameterValue} with the specified parameter and value. + * + * @param parameter the parameter associated with this value. + * @param value the value associated with the parameter. + * @return a new {@link ParameterValue} instance. + */ + @NotNull + static ParameterValue of(@NotNull final Parameter parameter, @Nullable final Object value) { + return new ParameterValueImpl(parameter, value); + } + + /** + * Creates a new instance of {@link ParameterValue} with the specified parameter, value, and descriptor. + * + * @param parameter the parameter associated with this value. + * @param value the value associated with the parameter. + * @param descriptor an optional descriptor for the parameter. + * @return a new {@link ParameterValue} instance. + */ + @NotNull + static ParameterValue of( + @NotNull final Parameter parameter, + @Nullable final Object value, + @Nullable final ParameterDescriptor descriptor + ) { + return new ParameterValueImpl(parameter, value, descriptor); + } + + /** + * Returns the parameter associated with this value. + * + * @return the parameter. + */ + @NotNull + Parameter parameter(); + + /** + * Returns the value associated with the parameter. + * + * @return the value. + */ + @Nullable + Object value(); + + /** + * Returns the descriptor for the parameter. + * + * @return the descriptor. + */ + @Nullable + ParameterDescriptor descriptor(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/ParameterValueImpl.java b/annotation/src/main/java/net/infumia/frame/annotation/ParameterValueImpl.java new file mode 100644 index 0000000..8af4b7d --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/ParameterValueImpl.java @@ -0,0 +1,26 @@ +package net.infumia.frame.annotation; + +import java.lang.reflect.Parameter; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +final class ParameterValueImpl implements ParameterValue { + + @NotNull + private final Parameter parameter; + + @Nullable + private final Object value; + + @Nullable + private final ParameterDescriptor descriptor; + + public ParameterValueImpl(@NotNull final Parameter parameter, @Nullable final Object value) { + this(parameter, value, null); + } +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigKeyProvider.java b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigKeyProvider.java new file mode 100644 index 0000000..186437a --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigKeyProvider.java @@ -0,0 +1,9 @@ +package net.infumia.frame.annotation.config; + +import net.infumia.frame.api.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ConfigKeyProvider { + @NotNull + String provideConfigKey(@NotNull ContextBase ctx); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProvider.java b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProvider.java new file mode 100644 index 0000000..7e49854 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProvider.java @@ -0,0 +1,19 @@ +package net.infumia.frame.annotation.config; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ConfigValueProvider { + @Nullable + Object getRaw(@NotNull String key); + + @Nullable + Integer getInt(@NotNull String key); + + @Nullable + String getString(@NotNull String key); + + @Nullable + ItemStack getItem(@NotNull String key); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderEmpty.java b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderEmpty.java new file mode 100644 index 0000000..2d1635e --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderEmpty.java @@ -0,0 +1,37 @@ +package net.infumia.frame.annotation.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ConfigValueProviderEmpty implements ConfigValueProvider { + + public static final ConfigValueProvider INSTANCE = new ConfigValueProviderEmpty(); + + @Nullable + @Override + public Object getRaw(@NotNull final String key) { + return null; + } + + @Nullable + @Override + public Integer getInt(@NotNull final String key) { + return null; + } + + @Nullable + @Override + public String getString(@NotNull final String key) { + return null; + } + + @Nullable + @Override + public ItemStack getItem(@NotNull final String key) { + return null; + } +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderMap.java b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderMap.java new file mode 100644 index 0000000..debf7aa --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/config/ConfigValueProviderMap.java @@ -0,0 +1,58 @@ +package net.infumia.frame.annotation.config; + +import java.util.Map; +import lombok.RequiredArgsConstructor; +import net.infumia.frame.api.util.Preconditions; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public final class ConfigValueProviderMap implements ConfigValueProvider { + + @NotNull + private final Map config; + + @Nullable + @Override + public Object getRaw(@NotNull final String key) { + return this.config.get(key); + } + + @Nullable + @Override + public Integer getInt(@NotNull final String key) { + final Object raw = this.getRaw(key); + if (raw == null) { + return null; + } + Preconditions.argument(raw instanceof Integer, "Raw value '%s' is not an integer!", raw); + return (int) raw; + } + + @Nullable + @Override + public String getString(@NotNull final String key) { + final Object raw = this.getRaw(key); + if (raw == null) { + return null; + } + Preconditions.argument(raw instanceof String, "Raw value '%s' is not a String!", raw); + return (String) raw; + } + + @Nullable + @Override + public ItemStack getItem(@NotNull final String key) { + final Object raw = this.getRaw(key); + if (raw == null) { + return null; + } + Preconditions.argument( + raw instanceof ItemStack, + "Raw value '%s' is not an ItemStack!", + raw + ); + return (ItemStack) raw; + } +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/Order.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/Order.java new file mode 100644 index 0000000..ac04903 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/Order.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Order { + int value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnClick.java new file mode 100644 index 0000000..d913ae7 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnClick.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewCancelOnClick { + boolean value() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrag.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrag.java new file mode 100644 index 0000000..884bbef --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrag.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewCancelOnDrag { + boolean value() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrop.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrop.java new file mode 100644 index 0000000..292f374 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnDrop.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewCancelOnDrop { + boolean value() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnPickup.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnPickup.java new file mode 100644 index 0000000..831e6c2 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewCancelOnPickup.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewCancelOnPickup { + boolean value() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewConfigModifier.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewConfigModifier.java new file mode 100644 index 0000000..9c5bff8 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/ViewConfigModifier.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +public @interface ViewConfigModifier { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelay.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelay.java new file mode 100644 index 0000000..ce3bbf4 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelay.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.interaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewInteractionDelay { + long value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayConfigKey.java new file mode 100644 index 0000000..e90a3c9 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.interaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewInteractionDelayConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvide.java new file mode 100644 index 0000000..a579f53 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.interaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewInteractionDelayProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvideConfigKey.java new file mode 100644 index 0000000..657dffa --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewInteractionDelayProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.interaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewInteractionDelayProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewOnInteractionDelay.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewOnInteractionDelay.java new file mode 100644 index 0000000..a03ccb8 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/interaction/ViewOnInteractionDelay.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.interaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnInteractionDelay { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayout.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayout.java new file mode 100644 index 0000000..a246c41 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayout.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewLayout { + String[] value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutConfigKey.java new file mode 100644 index 0000000..28c0542 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewLayoutConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvide.java new file mode 100644 index 0000000..6f886c7 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewLayoutProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvideConfigKey.java new file mode 100644 index 0000000..35af641 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewLayoutProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewLayoutProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewOnLayout.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewOnLayout.java new file mode 100644 index 0000000..c6c3117 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/layout/ViewOnLayout.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.layout; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnLayout { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewOnSize.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewOnSize.java new file mode 100644 index 0000000..aaf8434 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewOnSize.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.size; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnSize { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSize.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSize.java new file mode 100644 index 0000000..6d0b116 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSize.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.size; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewSize { + int value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeConfigKey.java new file mode 100644 index 0000000..64b180c --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.size; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewSizeConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvide.java new file mode 100644 index 0000000..fd30ef0 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.size; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewSizeProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvideConfigKey.java new file mode 100644 index 0000000..5b0bdea --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/size/ViewSizeProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.size; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewSizeProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewOnTitle.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewOnTitle.java new file mode 100644 index 0000000..957955f --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewOnTitle.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.title; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnTitle { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitle.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitle.java new file mode 100644 index 0000000..87de993 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitle.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.title; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTitle { + String value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleConfigKey.java new file mode 100644 index 0000000..d0721d9 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.title; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTitleConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvide.java new file mode 100644 index 0000000..e462ade --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.title; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTitleProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvideConfigKey.java new file mode 100644 index 0000000..b299a29 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/title/ViewTitleProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.title; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTitleProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewOnTransitiveInitialData.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewOnTransitiveInitialData.java new file mode 100644 index 0000000..0819a73 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewOnTransitiveInitialData.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.transitive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnTransitiveInitialData { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialData.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialData.java new file mode 100644 index 0000000..0f6defd --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialData.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.transitive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTransitiveInitialData { + boolean value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataConfigKey.java new file mode 100644 index 0000000..a4735c4 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.transitive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTransitiveInitialDataConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvide.java new file mode 100644 index 0000000..551d959 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.transitive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTransitiveInitialDataProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvideConfigKey.java new file mode 100644 index 0000000..e526b11 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/transitive/ViewTransitiveInitialDataProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.transitive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTransitiveInitialDataProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewOnType.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewOnType.java new file mode 100644 index 0000000..078d11a --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewOnType.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.type; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnType { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewType.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewType.java new file mode 100644 index 0000000..502e04e --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewType.java @@ -0,0 +1,13 @@ +package net.infumia.frame.annotation.decorator.config.type; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.type.InvType; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewType { + InvType value() default InvType.CHEST; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeConfigKey.java new file mode 100644 index 0000000..4124a2f --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.type; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTypeConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvide.java new file mode 100644 index 0000000..2b64985 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvide.java @@ -0,0 +1,17 @@ +package net.infumia.frame.annotation.decorator.config.type; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; +import net.infumia.frame.api.type.InvType; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTypeProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvideConfigKey.java new file mode 100644 index 0000000..2bedfcb --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/type/ViewTypeProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.type; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewTypeProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewOnUpdateInterval.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewOnUpdateInterval.java new file mode 100644 index 0000000..d8abb24 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewOnUpdateInterval.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.update; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnUpdateInterval { + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateInterval.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateInterval.java new file mode 100644 index 0000000..b481be0 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateInterval.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.config.update; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewUpdateInterval { + long value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalConfigKey.java new file mode 100644 index 0000000..9ade87e --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.config.update; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewUpdateIntervalConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvide.java new file mode 100644 index 0000000..ec06902 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvide.java @@ -0,0 +1,16 @@ +package net.infumia.frame.annotation.decorator.config.update; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; +import net.infumia.frame.api.context.ContextBase; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewUpdateIntervalProvide { + Class> value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvideConfigKey.java new file mode 100644 index 0000000..8e93620 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/config/update/ViewUpdateIntervalProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.config.update; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewUpdateIntervalProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementBack.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementBack.java new file mode 100644 index 0000000..7c97310 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementBack.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementBack { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCancelOnClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCancelOnClick.java new file mode 100644 index 0000000..481233b --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCancelOnClick.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementCancelOnClick { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCloseOnClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCloseOnClick.java new file mode 100644 index 0000000..89aef2d --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementCloseOnClick.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementCloseOnClick { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementNavigateOnClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementNavigateOnClick.java new file mode 100644 index 0000000..57b7fa6 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementNavigateOnClick.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementNavigateOnClick { + Class value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementUpdateOnClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementUpdateOnClick.java new file mode 100644 index 0000000..4fc78c6 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/ElementUpdateOnClick.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementUpdateOnClick { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementConfigKey.java new file mode 100644 index 0000000..f1c5155 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.itemstack; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideConfigKey.java new file mode 100644 index 0000000..ee6c203 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.itemstack; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideItemStack.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideItemStack.java new file mode 100644 index 0000000..c1ecefa --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/itemstack/ElementProvideItemStack.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.itemstack; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.element.provider.itemstack.ElementProviderItemStack; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementProvideItemStack { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationAdvance.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationAdvance.java new file mode 100644 index 0000000..a6ac297 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationAdvance.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.pagination; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementPaginationAdvance { + String value() default ""; + + boolean hideIfCannot() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationBack.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationBack.java new file mode 100644 index 0000000..a28eb38 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/pagination/ElementPaginationBack.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.pagination; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementPaginationBack { + String value() default ""; + + boolean hideIfCannot() default true; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotConfigKey.java new file mode 100644 index 0000000..46b4302 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotIndex.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotIndex.java new file mode 100644 index 0000000..89e93a0 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotIndex.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotIndex { + int value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotLayout.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotLayout.java new file mode 100644 index 0000000..4dc3750 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotLayout.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotLayout { + char value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotPosition.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotPosition.java new file mode 100644 index 0000000..11fe5a4 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotPosition.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotPosition { + int row(); + + int column(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideConfigKey.java new file mode 100644 index 0000000..1bea1fd --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideIndex.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideIndex.java new file mode 100644 index 0000000..c772b49 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideIndex.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.element.provider.slot.ElementSlotProviderIndex; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotProvideIndex { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideLayout.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideLayout.java new file mode 100644 index 0000000..9c0bc30 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvideLayout.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.element.provider.slot.ElementSlotProviderLayout; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotProvideLayout { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvidePosition.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvidePosition.java new file mode 100644 index 0000000..ff28556 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/slot/ElementSlotProvidePosition.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.slot; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.element.provider.slot.ElementSlotProviderPosition; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementSlotProvidePosition { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayout.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayout.java new file mode 100644 index 0000000..9105727 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayout.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultLayout { + char value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutConfigKey.java new file mode 100644 index 0000000..4f6122f --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutConfigKey.java @@ -0,0 +1,14 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultLayoutConfigKey { + String value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvide.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvide.java new file mode 100644 index 0000000..8b154c0 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvide.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.api.element.provider.vault.ElementVaultLayoutProvider; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultLayoutProvide { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvideConfigKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvideConfigKey.java new file mode 100644 index 0000000..61c3c1e --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLayoutProvideConfigKey.java @@ -0,0 +1,15 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.infumia.frame.annotation.config.ConfigKeyProvider; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultLayoutProvideConfigKey { + Class value(); + + boolean cache() default false; +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLoadOnOpen.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLoadOnOpen.java new file mode 100644 index 0000000..fc9a236 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultLoadOnOpen.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultLoadOnOpen { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultSaveOnClose.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultSaveOnClose.java new file mode 100644 index 0000000..8c25ac7 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/element/vault/ElementVaultSaveOnClose.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.element.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ElementVaultSaveOnClose { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/state/StateKey.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/state/StateKey.java new file mode 100644 index 0000000..ca3e961 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/state/StateKey.java @@ -0,0 +1,12 @@ +package net.infumia.frame.annotation.decorator.state; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface StateKey { + String value(); +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnClose.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnClose.java new file mode 100644 index 0000000..c683596 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnClose.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnClose { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnFirstRender.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnFirstRender.java new file mode 100644 index 0000000..145a9af --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnFirstRender.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnFirstRender { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnGlobalClick.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnGlobalClick.java new file mode 100644 index 0000000..69664f5 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnGlobalClick.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnGlobalClick { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnInit.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnInit.java new file mode 100644 index 0000000..91d6c59 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnInit.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnInit { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnOpen.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnOpen.java new file mode 100644 index 0000000..9b75f34 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnOpen.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnOpen { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnUpdate.java b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnUpdate.java new file mode 100644 index 0000000..489f536 --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/decorator/view/ViewOnUpdate.java @@ -0,0 +1,11 @@ +package net.infumia.frame.annotation.decorator.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ViewOnUpdate { +} diff --git a/annotation/src/main/java/net/infumia/frame/annotation/exception/AnnotatedMethodHandlerInitiationException.java b/annotation/src/main/java/net/infumia/frame/annotation/exception/AnnotatedMethodHandlerInitiationException.java new file mode 100644 index 0000000..a7e473e --- /dev/null +++ b/annotation/src/main/java/net/infumia/frame/annotation/exception/AnnotatedMethodHandlerInitiationException.java @@ -0,0 +1,10 @@ +package net.infumia.frame.annotation.exception; + +import lombok.experimental.StandardException; +import net.infumia.frame.annotation.AnnotatedMethodHandler; + +/** + * Exception thrown when there is an error during the initiation of {@link AnnotatedMethodHandler}s. + */ +@StandardException +public final class AnnotatedMethodHandlerInitiationException extends RuntimeException {} diff --git a/build.gradle.kts b/build.gradle.kts index 467db0e..3889c5d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,13 @@ import net.infumia.gradle.applySpotless plugins { java } -subprojects { apply() } +subprojects { + apply() + + repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + } +} applySpotless() diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 2dbda90..5792ebd 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,11 @@ import net.infumia.gradle.applyPublish applyPublish() + +dependencies { + compileOnly(libs.minecraft.one.eight.eight.paper) + + compileOnly(libs.guice) { isTransitive = false } + compileOnly(libs.geantyref) + compileOnly(libs.adventure.api) +} diff --git a/common/src/main/java/net/infumia/frame/Frame.java b/common/src/main/java/net/infumia/frame/Frame.java new file mode 100644 index 0000000..b735eaf --- /dev/null +++ b/common/src/main/java/net/infumia/frame/Frame.java @@ -0,0 +1,112 @@ +package net.infumia.frame; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.logger.Logger; +import net.infumia.frame.pipeline.Pipelined; +import net.infumia.frame.pipeline.executor.PipelineExecutorManager; +import net.infumia.frame.task.TaskFactory; +import net.infumia.frame.typedkey.TypedKeyStorageFactory; +import net.infumia.frame.typedkey.TypedKeyStorageImmutableBuilder; +import net.infumia.frame.view.ViewCreator; +import net.infumia.frame.view.creator.InventoryCreator; +import net.infumia.frame.viewer.ViewerCreator; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface Frame extends Pipelined { + @NotNull + static Frame create(@NotNull final Plugin plugin) { + return Internal.factory().create(plugin); + } + + @NotNull + static Frame create(@NotNull final Plugin plugin, final boolean unregisterOnDisable) { + return Internal.factory().create(plugin, unregisterOnDisable); + } + + @NotNull + static Frame create(@NotNull final Plugin plugin, @NotNull final Logger logger) { + return Internal.factory().create(plugin, logger); + } + + @NotNull + static Frame create( + @NotNull final Plugin plugin, + @NotNull final Logger logger, + final boolean unregisterOnDisable + ) { + return Internal.factory().create(plugin, logger, unregisterOnDisable); + } + + @NotNull + Logger logger(); + + @NotNull + TaskFactory taskFactory(); + + @NotNull + ViewCreator viewCreator(); + + @NotNull + ViewerCreator viewerCreator(); + + @NotNull + TypedKeyStorageFactory storageFactory(); + + void storageFactory(@NotNull TypedKeyStorageFactory storageFactory); + + @NotNull + InventoryCreator inventoryCreator(); + + void inventoryCreator(@NotNull InventoryCreator inventoryCreator); + + void register(); + + void unregister(); + + @NotNull + Frame with(@NotNull Class viewClass); + + @NotNull + CompletableFuture<@Nullable ContextRender> open( + @NotNull Player player, + @NotNull Class viewClass + ); + + @NotNull + CompletableFuture<@Nullable ContextRender> open( + @NotNull Player player, + @NotNull Class viewClass, + @NotNull Consumer initialDataConfigurer + ); + + @NotNull + CompletableFuture<@Nullable ContextRender> open( + @NotNull Collection players, + @NotNull Class viewClass + ); + + @NotNull + CompletableFuture<@Nullable ContextRender> open( + @NotNull Collection players, + @NotNull Class viewClass, + @NotNull Consumer initialDataConfigurer + ); + + @NotNull + CompletableFuture openActive( + @NotNull Player player, + @NotNull ContextRender activeContext + ); + + @NotNull + CompletableFuture openActive( + @NotNull Collection players, + @NotNull ContextRender activeContext + ); +} diff --git a/common/src/main/java/net/infumia/frame/FrameFactory.java b/common/src/main/java/net/infumia/frame/FrameFactory.java new file mode 100644 index 0000000..83757c1 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/FrameFactory.java @@ -0,0 +1,19 @@ +package net.infumia.frame; + +import net.infumia.frame.logger.Logger; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public interface FrameFactory { + @NotNull + Frame create(@NotNull Plugin plugin, @NotNull Logger logger, boolean unregisterOnDisable); + + @NotNull + Frame create(@NotNull Plugin plugin, @NotNull Logger logger); + + @NotNull + Frame create(@NotNull Plugin plugin, boolean unregisterOnDisable); + + @NotNull + Frame create(@NotNull Plugin plugin); +} diff --git a/common/src/main/java/net/infumia/frame/Internal.java b/common/src/main/java/net/infumia/frame/Internal.java new file mode 100644 index 0000000..5af6640 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/Internal.java @@ -0,0 +1,30 @@ +package net.infumia.frame; + +import net.infumia.frame.util.Reflection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class Internal { + + private static final String DEFAULT_FACTORY_CLASS = + "net.infumia.frame.InventoryManagerFactoryImpl"; + + @Nullable + private static FrameFactory factory; + + @NotNull + static FrameFactory factory() { + if (Internal.factory != null) { + return Internal.factory; + } + final FrameFactory found = Reflection.findInstanceFromField( + Internal.DEFAULT_FACTORY_CLASS, + "INSTANCE" + ); + return Internal.factory = found; + } + + private Internal() { + throw new IllegalStateException("Utility class!"); + } +} diff --git a/common/src/main/java/net/infumia/frame/context/Context.java b/common/src/main/java/net/infumia/frame/context/Context.java new file mode 100644 index 0000000..67553f9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/Context.java @@ -0,0 +1,17 @@ +package net.infumia.frame.context; + +import net.infumia.frame.Frame; +import net.infumia.frame.state.StateFactory; +import net.infumia.frame.typedkey.TypedKeyStorage; +import org.jetbrains.annotations.NotNull; + +public interface Context { + @NotNull + Frame manager(); + + @NotNull + TypedKeyStorage instances(); + + @NotNull + StateFactory stateFactory(); +} diff --git a/common/src/main/java/net/infumia/frame/context/ContextBase.java b/common/src/main/java/net/infumia/frame/context/ContextBase.java new file mode 100644 index 0000000..b885812 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/ContextBase.java @@ -0,0 +1,55 @@ +package net.infumia.frame.context; + +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.value.StateValueHostHolder; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.typedkey.TypedKeyStorageImmutableBuilder; +import net.infumia.frame.view.View; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ContextBase extends Context, StateValueHostHolder { + @NotNull + UUID id(); + + @NotNull + View view(); + + @NotNull + ViewConfig initialConfig(); + + @NotNull + TypedKeyStorageImmutable initialData(); + + @NotNull + Collection viewers(); + + @NotNull + Viewer viewer(); + + boolean sharedView(); + + @NotNull + CompletableFuture<@Nullable ContextRender> openForEveryone(@NotNull Class viewClass); + + @NotNull + CompletableFuture<@Nullable ContextRender> openForEveryone( + @NotNull Class viewClass, + @NotNull Consumer initialData + ); + + @NotNull + CompletableFuture<@Nullable ContextRender> openForViewer(@NotNull Class viewClass); + + @NotNull + CompletableFuture<@Nullable ContextRender> openForViewer( + @NotNull Class viewClass, + @NotNull Consumer initialDataConfigurer + ); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementClear.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementClear.java new file mode 100644 index 0000000..d97c94b --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementClear.java @@ -0,0 +1,10 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementClear extends ContextRender { + @NotNull + Element element(); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementClick.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementClick.java new file mode 100644 index 0000000..14bf6fa --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementClick.java @@ -0,0 +1,10 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementClick extends ContextClick { + @NotNull + Element element(); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementItemClick.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemClick.java new file mode 100644 index 0000000..aa22e56 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemClick.java @@ -0,0 +1,10 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementItemClick extends ContextElementClick { + @NotNull + @Override + ElementItem element(); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementItemRender.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemRender.java new file mode 100644 index 0000000..c12d54a --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemRender.java @@ -0,0 +1,20 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementItemRender extends ContextElementRender { + @NotNull + @Override + ElementItem element(); + + int modifiedSlot(); + + void modifySlot(int slot); + + @NotNull + ItemStack modifiedItem(); + + void modifyItem(@NotNull ItemStack newItem); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdate.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdate.java new file mode 100644 index 0000000..c7c5055 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdate.java @@ -0,0 +1,10 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementItemUpdate extends ContextElementUpdate { + @NotNull + @Override + ElementItem element(); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementRender.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementRender.java new file mode 100644 index 0000000..c45e840 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementRender.java @@ -0,0 +1,10 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementRender extends ContextRender { + @NotNull + Element element(); +} diff --git a/common/src/main/java/net/infumia/frame/context/element/ContextElementUpdate.java b/common/src/main/java/net/infumia/frame/context/element/ContextElementUpdate.java new file mode 100644 index 0000000..377b962 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/element/ContextElementUpdate.java @@ -0,0 +1,13 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import net.infumia.frame.service.Cancellable; +import org.jetbrains.annotations.NotNull; + +public interface ContextElementUpdate extends ContextRender, Cancellable { + @NotNull + Element element(); + + boolean forced(); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextClick.java b/common/src/main/java/net/infumia/frame/context/view/ContextClick.java new file mode 100644 index 0000000..067172f --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextClick.java @@ -0,0 +1,49 @@ +package net.infumia.frame.context.view; + +import net.infumia.frame.service.Cancellable; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.viewer.ContextualViewer; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ContextClick extends ContextRender, Cancellable { + @NotNull + InventoryClickEvent event(); + + @NotNull + ContextualViewer clicker(); + + @NotNull + ClickType clickType(); + + @Nullable + ViewContainer clickedContainer(); + + int clickedSlot(); + + int clickedSlotRaw(); + + @NotNull + InventoryType.SlotType clickedSlotType(); + + boolean leftClick(); + + boolean rightClick(); + + boolean middleClick(); + + boolean shiftClick(); + + boolean keyboardClick(); + + boolean outsideClicked(); + + boolean entityContainer(); + + boolean layoutSlot(); + + boolean layoutSlot(char character); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextClose.java b/common/src/main/java/net/infumia/frame/context/view/ContextClose.java new file mode 100644 index 0000000..6a36b9f --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextClose.java @@ -0,0 +1,13 @@ +package net.infumia.frame.context.view; + +import net.infumia.frame.service.Cancellable; +import net.infumia.frame.viewer.ContextualViewer; +import org.jetbrains.annotations.NotNull; + +public interface ContextClose extends ContextRender, Cancellable { + @NotNull + @Override + ContextualViewer viewer(); + + boolean forced(); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextInit.java b/common/src/main/java/net/infumia/frame/context/view/ContextInit.java new file mode 100644 index 0000000..30e3b9c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextInit.java @@ -0,0 +1,17 @@ +package net.infumia.frame.context.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.Context; +import net.infumia.frame.view.config.ViewConfigBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ContextInit extends Context { + @NotNull + ViewConfigBuilder configBuilder(); + + void waitUntil(@Nullable CompletableFuture waitUntil); + + @Nullable + CompletableFuture waitUntil(); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextOpen.java b/common/src/main/java/net/infumia/frame/context/view/ContextOpen.java new file mode 100644 index 0000000..db00e09 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextOpen.java @@ -0,0 +1,22 @@ +package net.infumia.frame.context.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.service.Cancellable; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.view.config.ViewConfigBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ContextOpen extends ContextBase, Cancellable { + void waitUntil(@NotNull CompletableFuture future); + + @Nullable + CompletableFuture waitUntil(); + + @NotNull + ViewConfigBuilder modifyConfig(); + + @NotNull + ViewConfig buildFinalConfig(); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextRender.java b/common/src/main/java/net/infumia/frame/context/view/ContextRender.java new file mode 100644 index 0000000..ec48762 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextRender.java @@ -0,0 +1,44 @@ +package net.infumia.frame.context.view; + +import java.util.Map; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.element.ElementContainer; +import net.infumia.frame.element.ElementItemBuilderFactory; +import net.infumia.frame.pipeline.Pipelined; +import net.infumia.frame.pipeline.executor.PipelineExecutorRender; +import net.infumia.frame.pipeline.executor.PipelineExecutorViewer; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import org.jetbrains.annotations.NotNull; + +public interface ContextRender + extends + ContextBase, + ElementItemBuilderFactory, + ElementContainer, + Pipelined { + @NotNull + ViewContainer container(); + + @NotNull + ViewConfig config(); + + @NotNull + Map layouts(); + + void back(); + + boolean canBack(); + + void closeForEveryone(); + + void closeForViewer(); + + void closeForEveryone(boolean forced); + + void closeForViewer(boolean forced); + + @NotNull + PipelineExecutorViewer pipelinesViewer(); +} diff --git a/common/src/main/java/net/infumia/frame/context/view/ContextResume.java b/common/src/main/java/net/infumia/frame/context/view/ContextResume.java new file mode 100644 index 0000000..64a165e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/context/view/ContextResume.java @@ -0,0 +1,13 @@ +package net.infumia.frame.context.view; + +import java.util.Collection; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface ContextResume extends ContextRender { + @NotNull + ContextRender from(); + + @NotNull + Collection resumedViewers(); +} diff --git a/common/src/main/java/net/infumia/frame/element/Element.java b/common/src/main/java/net/infumia/frame/element/Element.java new file mode 100644 index 0000000..3b4afad --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/Element.java @@ -0,0 +1,24 @@ +package net.infumia.frame.element; + +import java.util.Collection; +import java.util.function.Predicate; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.Nullable; + +public interface Element { + boolean cancelOnClick(); + + boolean closeOnClick(); + + boolean updateOnClick(); + + @Nullable + Predicate displayIf(); + + @Nullable + Collection> updateOnStateChange(); + + @Nullable + Collection> updateOnStateAccess(); +} diff --git a/common/src/main/java/net/infumia/frame/element/ElementBuilder.java b/common/src/main/java/net/infumia/frame/element/ElementBuilder.java new file mode 100644 index 0000000..f550a76 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/ElementBuilder.java @@ -0,0 +1,45 @@ +package net.infumia.frame.element; + +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; + +public interface ElementBuilder { + @NotNull + ElementBuilder cancelOnClick(); + + @NotNull + ElementBuilder closeOnClick(); + + @NotNull + ElementBuilder updateOnClick(); + + @NotNull + ElementBuilder cancelOnClick(boolean cancelOnClick); + + @NotNull + ElementBuilder closeOnClick(boolean cancelOnClick); + + @NotNull + ElementBuilder updateOnClick(boolean updateOnClick); + + @NotNull + ElementBuilder updateOnStateChange(@NotNull State state, @NotNull State... otherStates); + + @NotNull + ElementBuilder updateOnStateAccess(@NotNull State state, @NotNull State... otherStates); + + @NotNull + ElementBuilder displayIf(@NotNull Predicate condition); + + @NotNull + ElementBuilder displayIf(@NotNull BooleanSupplier condition); + + @NotNull + ElementBuilder hideIf(@NotNull Predicate condition); + + @NotNull + ElementBuilder hideIf(@NotNull BooleanSupplier condition); +} diff --git a/common/src/main/java/net/infumia/frame/element/ElementContainer.java b/common/src/main/java/net/infumia/frame/element/ElementContainer.java new file mode 100644 index 0000000..ae35c52 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/ElementContainer.java @@ -0,0 +1,11 @@ +package net.infumia.frame.element; + +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; + +public interface ElementContainer { + @NotNull + @UnmodifiableView + List elements(); +} diff --git a/common/src/main/java/net/infumia/frame/element/ElementItem.java b/common/src/main/java/net/infumia/frame/element/ElementItem.java new file mode 100644 index 0000000..8521d34 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/ElementItem.java @@ -0,0 +1,25 @@ +package net.infumia.frame.element; + +import java.util.function.Consumer; +import net.infumia.frame.context.element.ContextElementItemClick; +import net.infumia.frame.context.element.ContextElementItemRender; +import net.infumia.frame.context.element.ContextElementItemUpdate; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ElementItem extends Element { + @NotNull + ItemStack item(); + + int slot(); + + @Nullable + Consumer onClick(); + + @Nullable + Consumer onRender(); + + @Nullable + Consumer onUpdate(); +} diff --git a/common/src/main/java/net/infumia/frame/element/ElementItemBuilder.java b/common/src/main/java/net/infumia/frame/element/ElementItemBuilder.java new file mode 100644 index 0000000..1c087ba --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/ElementItemBuilder.java @@ -0,0 +1,80 @@ +package net.infumia.frame.element; + +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import net.infumia.frame.context.element.ContextElementItemClick; +import net.infumia.frame.context.element.ContextElementItemRender; +import net.infumia.frame.context.element.ContextElementItemUpdate; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.State; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface ElementItemBuilder extends ElementBuilder { + @NotNull + ElementItemBuilder item(@NotNull ItemStack item); + + @NotNull + ElementItemBuilder slot(int slot); + + @NotNull + ElementItemBuilder onClick(@NotNull Consumer onClick); + + @NotNull + ElementItemBuilder onClick(@NotNull Runnable onClick); + + @NotNull + ElementItemBuilder onRender(@NotNull Consumer onRender); + + @NotNull + ElementItemBuilder renderWith( + @NotNull Function renderWith + ); + + @NotNull + ElementItemBuilder onUpdate(@NotNull Consumer onUpdate); + + @NotNull + @Override + ElementItemBuilder cancelOnClick(); + + @NotNull + @Override + ElementItemBuilder closeOnClick(); + + @NotNull + @Override + ElementItemBuilder updateOnClick(); + + @NotNull + @Override + ElementItemBuilder updateOnStateChange( + @NotNull State state, + @NotNull State @NotNull... otherStates + ); + + @NotNull + @Override + ElementItemBuilder updateOnStateAccess( + @NotNull State state, + @NotNull State @NotNull... otherStates + ); + + @NotNull + @Override + ElementItemBuilder displayIf(@NotNull Predicate condition); + + @NotNull + @Override + ElementItemBuilder displayIf(@NotNull BooleanSupplier condition); + + @NotNull + @Override + ElementItemBuilder hideIf(@NotNull Predicate condition); + + @NotNull + @Override + ElementItemBuilder hideIf(@NotNull BooleanSupplier condition); +} diff --git a/common/src/main/java/net/infumia/frame/element/ElementItemBuilderFactory.java b/common/src/main/java/net/infumia/frame/element/ElementItemBuilderFactory.java new file mode 100644 index 0000000..a77596a --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/ElementItemBuilderFactory.java @@ -0,0 +1,40 @@ +package net.infumia.frame.element; + +import java.util.function.BiConsumer; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface ElementItemBuilderFactory { + @NotNull + ElementItemBuilder unsetSlot(); + + @NotNull + ElementItemBuilder layout(char layout); + + @NotNull + ElementItemBuilder layout(char layout, @NotNull ItemStack item); + + void layout(char layout, @NotNull BiConsumer configurer); + + @NotNull + ElementItemBuilder slot(int slot); + + @NotNull + ElementItemBuilder position(int row, int column); + + @NotNull + ElementItemBuilder firstSlot(); + + @NotNull + ElementItemBuilder lastSlot(); + + void availableSlot(@NotNull ItemStack item); + + void availableSlot(@NotNull BiConsumer configurer); + + @NotNull + ElementItemBuilder resultSlot(); + + @NotNull + ElementItemBuilder resultSlot(@NotNull ItemStack item); +} diff --git a/common/src/main/java/net/infumia/frame/element/pagination/ElementPagination.java b/common/src/main/java/net/infumia/frame/element/pagination/ElementPagination.java new file mode 100644 index 0000000..df03df7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/pagination/ElementPagination.java @@ -0,0 +1,34 @@ +package net.infumia.frame.element.pagination; + +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementContainer; + +public interface ElementPagination extends Element, ElementContainer { + int currentPageIndex(); + + int nextPageIndex(); + + int previousPageIndex(); + + int lastPageIndex(); + + boolean isFirstPage(); + + boolean isLastPage(); + + int elementCount(); + + int pageCount(); + + boolean hasPage(int pageIndex); + + void switchTo(int pageIndex); + + void advance(); + + boolean canAdvance(); + + void back(); + + boolean canBack(); +} diff --git a/common/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilder.java b/common/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilder.java new file mode 100644 index 0000000..c8021df --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilder.java @@ -0,0 +1,88 @@ +package net.infumia.frame.element.pagination; + +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.ElementBuilder; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.state.State; +import net.infumia.frame.state.pagination.ElementConfigurer; +import net.infumia.frame.state.pagination.StatePagination; +import org.jetbrains.annotations.NotNull; + +public interface ElementPaginationBuilder extends ElementBuilder { + @NotNull + ElementPaginationBuilder layout(char layout); + + @NotNull + ElementPaginationBuilder onPageSwitch( + @NotNull BiConsumer onPageSwitch + ); + + @NotNull + ElementPaginationBuilder elementConfigurer( + @NotNull BiConsumer configurer + ); + + @NotNull + ElementPaginationBuilder elementConfigurer(@NotNull ElementConfigurer configurer); + + @NotNull + StatePagination buildPagination(); + + @NotNull + @Override + ElementPaginationBuilder cancelOnClick(); + + @NotNull + @Override + ElementPaginationBuilder closeOnClick(); + + @NotNull + @Override + ElementPaginationBuilder updateOnClick(); + + @NotNull + @Override + ElementPaginationBuilder cancelOnClick(boolean cancelOnClick); + + @NotNull + @Override + ElementPaginationBuilder closeOnClick(boolean cancelOnClick); + + @NotNull + @Override + ElementPaginationBuilder updateOnClick(boolean updateOnClick); + + @NotNull + @Override + ElementPaginationBuilder updateOnStateChange( + @NotNull State state, + @NotNull State... otherStates + ); + + @NotNull + @Override + ElementPaginationBuilder updateOnStateAccess( + @NotNull State state, + @NotNull State... otherStates + ); + + @NotNull + @Override + ElementPaginationBuilder displayIf(@NotNull Predicate condition); + + @NotNull + @Override + ElementPaginationBuilder displayIf(@NotNull BooleanSupplier condition); + + @NotNull + @Override + ElementPaginationBuilder hideIf(@NotNull Predicate condition); + + @NotNull + @Override + ElementPaginationBuilder hideIf(@NotNull BooleanSupplier condition); +} diff --git a/common/src/main/java/net/infumia/frame/element/provider/itemstack/ElementProviderItemStack.java b/common/src/main/java/net/infumia/frame/element/provider/itemstack/ElementProviderItemStack.java new file mode 100644 index 0000000..019c2d6 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/provider/itemstack/ElementProviderItemStack.java @@ -0,0 +1,10 @@ +package net.infumia.frame.element.provider.itemstack; + +import net.infumia.frame.context.ContextBase; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface ElementProviderItemStack { + @NotNull + ItemStack provideItemStack(@NotNull ContextBase ctx); +} diff --git a/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderIndex.java b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderIndex.java new file mode 100644 index 0000000..4583c3f --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderIndex.java @@ -0,0 +1,8 @@ +package net.infumia.frame.element.provider.slot; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementSlotProviderIndex { + int provideIndex(@NotNull ContextBase ctx); +} diff --git a/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderLayout.java b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderLayout.java new file mode 100644 index 0000000..777dc46 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderLayout.java @@ -0,0 +1,8 @@ +package net.infumia.frame.element.provider.slot; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementSlotProviderLayout { + char provideLayout(@NotNull ContextBase ctx); +} diff --git a/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderPosition.java b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderPosition.java new file mode 100644 index 0000000..3e92043 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/provider/slot/ElementSlotProviderPosition.java @@ -0,0 +1,10 @@ +package net.infumia.frame.element.provider.slot; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementSlotProviderPosition { + int provideRow(@NotNull ContextBase ctx); + + int provideColumn(@NotNull ContextBase ctx); +} diff --git a/common/src/main/java/net/infumia/frame/element/provider/vault/ElementVaultLayoutProvider.java b/common/src/main/java/net/infumia/frame/element/provider/vault/ElementVaultLayoutProvider.java new file mode 100644 index 0000000..bc7dbbd --- /dev/null +++ b/common/src/main/java/net/infumia/frame/element/provider/vault/ElementVaultLayoutProvider.java @@ -0,0 +1,8 @@ +package net.infumia.frame.element.provider.vault; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementVaultLayoutProvider { + char provideLayout(@NotNull ContextBase ctx); +} diff --git a/common/src/main/java/net/infumia/frame/extension/PipelineExtensions.java b/common/src/main/java/net/infumia/frame/extension/PipelineExtensions.java new file mode 100644 index 0000000..affdbf7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/extension/PipelineExtensions.java @@ -0,0 +1,18 @@ +package net.infumia.frame.extension; + +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.service.Implementation; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExtensions { + + @NotNull + public static PipelineConsumer register( + @NotNull final PipelineConsumer pipeline, + @NotNull final PipelineServiceConsumer service + ) { + return pipeline.apply(Implementation.register(service)); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/AnnotationAccessor.java b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessor.java new file mode 100644 index 0000000..f29fad5 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessor.java @@ -0,0 +1,30 @@ +package net.infumia.frame.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Collection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface AnnotationAccessor { + @NotNull + static AnnotationAccessor empty() { + return AnnotationAccessorEmpty.INSTANCE; + } + + @NotNull + static AnnotationAccessor of(@NotNull final AnnotatedElement element) { + return new AnnotationAccessorAnnotated(element); + } + + @NotNull + static AnnotationAccessor of(@NotNull final AnnotationAccessor @NotNull... accessors) { + return new AnnotationAccessorMultiDelegate(accessors); + } + + @Nullable + A annotation(@NotNull Class cls); + + @NotNull + Collection annotations(); +} diff --git a/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorAnnotated.java b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorAnnotated.java new file mode 100644 index 0000000..c5e44a0 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorAnnotated.java @@ -0,0 +1,31 @@ +package net.infumia.frame.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class AnnotationAccessorAnnotated implements AnnotationAccessor { + + @NotNull + private final AnnotatedElement element; + + AnnotationAccessorAnnotated(@NotNull final AnnotatedElement element) { + this.element = element; + } + + @Nullable + @Override + public A annotation(@NotNull final Class cls) { + return this.element.getAnnotation(cls); + } + + @NotNull + @Override + public Collection annotations() { + return Collections.unmodifiableCollection(Arrays.asList(this.element.getAnnotations())); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorEmpty.java b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorEmpty.java new file mode 100644 index 0000000..b29352c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorEmpty.java @@ -0,0 +1,26 @@ +package net.infumia.frame.injection; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class AnnotationAccessorEmpty implements AnnotationAccessor { + + static final AnnotationAccessor INSTANCE = new AnnotationAccessorEmpty(); + + private AnnotationAccessorEmpty() {} + + @Nullable + @Override + public A annotation(@NotNull final Class cls) { + return null; + } + + @NotNull + @Override + public Collection annotations() { + return Collections.emptyList(); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorMultiDelegate.java b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorMultiDelegate.java new file mode 100644 index 0000000..f77e0fd --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/AnnotationAccessorMultiDelegate.java @@ -0,0 +1,41 @@ +package net.infumia.frame.injection; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Objects; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class AnnotationAccessorMultiDelegate implements AnnotationAccessor { + + @NotNull + private final AnnotationAccessor@NotNull[] accessors; + + AnnotationAccessorMultiDelegate(final @NotNull AnnotationAccessor@NotNull[] accessors) { + this.accessors = accessors; + } + + @Nullable + @Override + public A annotation(@NotNull final Class cls) { + return Arrays.stream(this.accessors) + .map(accessor -> accessor.annotation(cls)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + @NotNull + @Override + public Collection annotations() { + return Collections.unmodifiableCollection( + Arrays.stream(this.accessors) + .flatMap(accessor -> accessor.annotations().stream()) + .collect(Collectors.toCollection(LinkedList::new)) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionRequester.java b/common/src/main/java/net/infumia/frame/injection/InjectionRequester.java new file mode 100644 index 0000000..b205ec5 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionRequester.java @@ -0,0 +1,27 @@ +package net.infumia.frame.injection; + +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletableFuture; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface InjectionRequester { + @NotNull + static InjectionRequester create(@NotNull final InjectionServicePipeline pipeline) { + return new InjectionRequesterImpl<>(pipeline); + } + + @NotNull + CompletableFuture<@Nullable T> request( + @NotNull Class cls, + @NotNull C context, + @NotNull AnnotationAccessor annotationAccessor + ); + + @NotNull + CompletableFuture<@Nullable T> request( + @NotNull TypeToken type, + @NotNull C context, + @NotNull AnnotationAccessor annotationAccessor + ); +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionRequesterImpl.java b/common/src/main/java/net/infumia/frame/injection/InjectionRequesterImpl.java new file mode 100644 index 0000000..f562f59 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionRequesterImpl.java @@ -0,0 +1,55 @@ +package net.infumia.frame.injection; + +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injector.InjectionRequest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class InjectionRequesterImpl implements InjectionRequester { + + @NotNull + private final InjectionServicePipeline pipeline; + + InjectionRequesterImpl(@NotNull final InjectionServicePipeline pipeline) { + this.pipeline = pipeline; + } + + @NotNull + @Override + public CompletableFuture<@Nullable T> request( + @NotNull final Class cls, + @NotNull final C context, + @NotNull final AnnotationAccessor annotationAccessor + ) { + return this.request(TypeToken.get(cls), context, annotationAccessor); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public CompletableFuture<@Nullable T> request( + @NotNull final TypeToken type, + @NotNull final C context, + @NotNull final AnnotationAccessor annotationAccessor + ) { + final InjectionRequest request = InjectionRequest.of(type, context, annotationAccessor); + return this.pipeline.completeWith(request).thenApply(rawResult -> { + if (rawResult == null) { + return null; + } + final Class injectedClass = request.injectedClass(); + if (injectedClass.isInstance(rawResult)) { + return (T) rawResult; + } else { + throw new IllegalArgumentException( + String.format( + "Injector returned type %s which is not an instance of %s", + rawResult.getClass().getName(), + injectedClass.getName() + ) + ); + } + }); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionService.java b/common/src/main/java/net/infumia/frame/injection/InjectionService.java new file mode 100644 index 0000000..496d083 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionService.java @@ -0,0 +1,14 @@ +package net.infumia.frame.injection; + +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.injector.InjectorRegistry; +import net.infumia.frame.service.Service; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface InjectionService extends Service, @Nullable Object> { + @NotNull + static InjectionService create(@NotNull final InjectorRegistry registry) { + return new InjectionServiceInjector<>(registry); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionServiceInjector.java b/common/src/main/java/net/infumia/frame/injection/InjectionServiceInjector.java new file mode 100644 index 0000000..ac4a4d2 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionServiceInjector.java @@ -0,0 +1,38 @@ +package net.infumia.frame.injection; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.injector.InjectorRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class InjectionServiceInjector implements InjectionService { + + public static final String KEY = "injector"; + + @NotNull + private final InjectorRegistry registry; + + InjectionServiceInjector(@NotNull final InjectorRegistry registry) { + this.registry = registry; + } + + @Override + public String key() { + return InjectionServiceInjector.KEY; + } + + @NotNull + @Override + public CompletableFuture<@Nullable Object> handle(@NotNull final InjectionRequest request) { + return CompletableFuture.completedFuture( + this.registry.injectors(request.injectedType()) + .stream() + .map(injector -> injector.inject(request)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionServicePipeline.java b/common/src/main/java/net/infumia/frame/injection/InjectionServicePipeline.java new file mode 100644 index 0000000..dc5f4b2 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionServicePipeline.java @@ -0,0 +1,29 @@ +package net.infumia.frame.injection; + +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.service.ServicePipelineBuilder; +import org.jetbrains.annotations.NotNull; + +public interface InjectionServicePipeline { + @NotNull + static InjectionServicePipeline create( + @NotNull final InjectionService defaultService + ) { + return new InjectionServicePipelineImpl<>( + ServicePipelineBuilder.newBuilder() + .build() + .create(new TypeToken>() {}, defaultService) + ); + } + + void apply(@NotNull Implementation, Object> operation); + + @NotNull + CompletableFuture completeWith(@NotNull InjectionRequest request); + + @NotNull + CompletableFuture completeWithAsync(@NotNull InjectionRequest request); +} diff --git a/common/src/main/java/net/infumia/frame/injection/InjectionServicePipelineImpl.java b/common/src/main/java/net/infumia/frame/injection/InjectionServicePipelineImpl.java new file mode 100644 index 0000000..0b794ff --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injection/InjectionServicePipelineImpl.java @@ -0,0 +1,36 @@ +package net.infumia.frame.injection; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.service.ServiceRepository; +import org.jetbrains.annotations.NotNull; + +final class InjectionServicePipelineImpl implements InjectionServicePipeline { + + @NotNull + private final ServiceRepository, Object> delegate; + + InjectionServicePipelineImpl( + @NotNull final ServiceRepository, Object> delegate + ) { + this.delegate = delegate; + } + + @Override + public void apply(@NotNull final Implementation, Object> operation) { + this.delegate.apply(operation); + } + + @NotNull + @Override + public CompletableFuture completeWith(@NotNull final InjectionRequest request) { + return this.delegate.completeWith(request); + } + + @NotNull + @Override + public CompletableFuture completeWithAsync(@NotNull final InjectionRequest request) { + return this.delegate.completeWithAsync(request); + } +} diff --git a/common/src/main/java/net/infumia/frame/injector/InjectionRequest.java b/common/src/main/java/net/infumia/frame/injector/InjectionRequest.java new file mode 100644 index 0000000..1bb57c9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/InjectionRequest.java @@ -0,0 +1,31 @@ +package net.infumia.frame.injector; + +import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.injection.AnnotationAccessor; +import org.jetbrains.annotations.NotNull; + +public interface InjectionRequest { + @NotNull + static InjectionRequest of( + @NotNull final TypeToken injectedType, + @NotNull final C context, + @NotNull final AnnotationAccessor annotationAccessor + ) { + return new InjectionRequestImpl<>(injectedType, context, annotationAccessor); + } + + @NotNull + TypeToken injectedType(); + + @NotNull + default Class injectedClass() { + return GenericTypeReflector.erase(this.injectedType().getType()); + } + + @NotNull + C context(); + + @NotNull + AnnotationAccessor annotationAccessor(); +} diff --git a/common/src/main/java/net/infumia/frame/injector/InjectionRequestImpl.java b/common/src/main/java/net/infumia/frame/injector/InjectionRequestImpl.java new file mode 100644 index 0000000..8a2c5c1 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/InjectionRequestImpl.java @@ -0,0 +1,45 @@ +package net.infumia.frame.injector; + +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.injection.AnnotationAccessor; +import org.jetbrains.annotations.NotNull; + +final class InjectionRequestImpl implements InjectionRequest { + + @NotNull + private final TypeToken injectedType; + + @NotNull + private final C context; + + @NotNull + private final AnnotationAccessor annotationAccessor; + + InjectionRequestImpl( + @NotNull final TypeToken injectedType, + @NotNull final C context, + @NotNull final AnnotationAccessor annotationAccessor + ) { + this.injectedType = injectedType; + this.context = context; + this.annotationAccessor = annotationAccessor; + } + + @NotNull + @Override + public TypeToken injectedType() { + return this.injectedType; + } + + @NotNull + @Override + public C context() { + return this.context; + } + + @NotNull + @Override + public AnnotationAccessor annotationAccessor() { + return this.annotationAccessor; + } +} diff --git a/common/src/main/java/net/infumia/frame/injector/Injector.java b/common/src/main/java/net/infumia/frame/injector/Injector.java new file mode 100644 index 0000000..6c25d68 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/Injector.java @@ -0,0 +1,10 @@ +package net.infumia.frame.injector; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface Injector { + @Nullable + Object inject(@NotNull InjectionRequest ctx); +} diff --git a/common/src/main/java/net/infumia/frame/injector/InjectorRegistry.java b/common/src/main/java/net/infumia/frame/injector/InjectorRegistry.java new file mode 100644 index 0000000..dc9d510 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/InjectorRegistry.java @@ -0,0 +1,31 @@ +package net.infumia.frame.injector; + +import io.leangen.geantyref.TypeToken; +import java.util.Collection; +import java.util.function.Predicate; +import org.jetbrains.annotations.NotNull; + +public interface InjectorRegistry { + @NotNull + static InjectorRegistry create() { + return new InjectorRegistryImpl<>(); + } + + @NotNull + InjectorRegistry register(@NotNull Injector injector); + + @NotNull + InjectorRegistry register(@NotNull Class cls, @NotNull Injector injector); + + @NotNull + InjectorRegistry register(@NotNull TypeToken type, @NotNull Injector injector); + + @NotNull + InjectorRegistry register( + @NotNull Predicate> predicate, + @NotNull Injector injector + ); + + @NotNull + Collection> injectors(@NotNull TypeToken type); +} diff --git a/common/src/main/java/net/infumia/frame/injector/InjectorRegistryImpl.java b/common/src/main/java/net/infumia/frame/injector/InjectorRegistryImpl.java new file mode 100644 index 0000000..bc93c26 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/InjectorRegistryImpl.java @@ -0,0 +1,67 @@ +package net.infumia.frame.injector; + +import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.geantyref.TypeToken; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.infumia.frame.util.Pair; +import org.jetbrains.annotations.NotNull; + +final class InjectorRegistryImpl implements InjectorRegistry { + + private final List>, Injector>> providers = new ArrayList<>(); + + InjectorRegistryImpl() {} + + @NotNull + @Override + public InjectorRegistry register(@NotNull final Injector injector) { + return this.register(__ -> true, injector); + } + + @NotNull + @Override + public synchronized InjectorRegistry register( + @NotNull final Class cls, + @NotNull final Injector injector + ) { + return this.register(TypeToken.get(cls), injector); + } + + @NotNull + @Override + public synchronized InjectorRegistry register( + @NotNull final TypeToken type, + @NotNull final Injector injector + ) { + return this.register( + cl -> GenericTypeReflector.isSuperType(cl.getType(), type.getType()), + injector + ); + } + + @NotNull + @Override + public synchronized InjectorRegistry register( + @NotNull final Predicate> predicate, + @NotNull final Injector injector + ) { + this.providers.add(Pair.of(predicate, injector)); + return this; + } + + @NotNull + @Override + public synchronized Collection> injectors(@NotNull final TypeToken type) { + return Collections.unmodifiableCollection( + this.providers.stream() + .filter(pair -> pair.first().test(type)) + .map(Pair::second) + .collect(Collectors.toList()) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/injector/guice/InjectorGuice.java b/common/src/main/java/net/infumia/frame/injector/guice/InjectorGuice.java new file mode 100644 index 0000000..e1e1de0 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/guice/InjectorGuice.java @@ -0,0 +1,40 @@ +package net.infumia.frame.injector.guice; + +import com.google.inject.ConfigurationException; +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.injector.Injector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class InjectorGuice implements Injector { + + @NotNull + private final com.google.inject.Injector injector; + + @NotNull + private final KeyCreator keyCreator; + + public InjectorGuice( + @NotNull final com.google.inject.Injector injector, + @NotNull final KeyCreator keyCreator + ) { + this.injector = injector; + this.keyCreator = keyCreator; + } + + public InjectorGuice(@NotNull final com.google.inject.Injector injector) { + this(injector, KeyCreator.firstBinding()); + } + + @Nullable + @Override + public Object inject(@NotNull final InjectionRequest ctx) { + try { + return this.injector.getInstance( + this.keyCreator.create(ctx.injectedClass(), ctx.annotationAccessor()) + ); + } catch (final ConfigurationException ignored) { + return null; + } + } +} diff --git a/common/src/main/java/net/infumia/frame/injector/guice/KeyCreator.java b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreator.java new file mode 100644 index 0000000..0e01cff --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreator.java @@ -0,0 +1,21 @@ +package net.infumia.frame.injector.guice; + +import com.google.inject.Key; +import net.infumia.frame.injection.AnnotationAccessor; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface KeyCreator { + @NotNull + static KeyCreator noBinding() { + return KeyCreatorNoBinding.INSTANCE; + } + + @NotNull + static KeyCreator firstBinding() { + return KeyCreatorFirstBinding.INSTANCE; + } + + @NotNull + Key create(@NotNull Class cls, @NotNull AnnotationAccessor accessor); +} diff --git a/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorFirstBinding.java b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorFirstBinding.java new file mode 100644 index 0000000..fbd9d3d --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorFirstBinding.java @@ -0,0 +1,28 @@ +package net.infumia.frame.injector.guice; + +import com.google.inject.BindingAnnotation; +import com.google.inject.Key; +import net.infumia.frame.injection.AnnotationAccessor; +import org.jetbrains.annotations.NotNull; + +final class KeyCreatorFirstBinding implements KeyCreator { + + static final KeyCreator INSTANCE = new KeyCreatorFirstBinding(); + + private KeyCreatorFirstBinding() {} + + @NotNull + @Override + public Key create( + @NotNull final Class cls, + @NotNull final AnnotationAccessor accessor + ) { + return accessor + .annotations() + .stream() + .filter(a -> a.annotationType().isAnnotationPresent(BindingAnnotation.class)) + .findFirst() + .map(a -> Key.get(cls, a)) + .orElseGet(() -> Key.get(cls)); + } +} diff --git a/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorNoBinding.java b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorNoBinding.java new file mode 100644 index 0000000..9df2718 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/injector/guice/KeyCreatorNoBinding.java @@ -0,0 +1,21 @@ +package net.infumia.frame.injector.guice; + +import com.google.inject.Key; +import net.infumia.frame.injection.AnnotationAccessor; +import org.jetbrains.annotations.NotNull; + +final class KeyCreatorNoBinding implements KeyCreator { + + static final KeyCreator INSTANCE = new KeyCreatorNoBinding(); + + private KeyCreatorNoBinding() {} + + @NotNull + @Override + public Key create( + @NotNull final Class cls, + @NotNull final AnnotationAccessor accessor + ) { + return Key.get(cls); + } +} diff --git a/common/src/main/java/net/infumia/frame/logger/Logger.java b/common/src/main/java/net/infumia/frame/logger/Logger.java new file mode 100644 index 0000000..013833e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/logger/Logger.java @@ -0,0 +1,23 @@ +package net.infumia.frame.logger; + +import org.jetbrains.annotations.NotNull; + +public interface Logger { + boolean isDebugEnabled(); + + void enableDebug(boolean enable); + + void error( + @NotNull Throwable throwable, + @NotNull String message, + @NotNull Object @NotNull... args + ); + + void error(@NotNull String message, @NotNull Object @NotNull... args); + + void warn(@NotNull String message, @NotNull Object @NotNull... args); + + void info(@NotNull String message, @NotNull Object @NotNull... args); + + void debug(@NotNull String message, @NotNull Object @NotNull... args); +} diff --git a/common/src/main/java/net/infumia/frame/metadata/CacheKeyExtractor.java b/common/src/main/java/net/infumia/frame/metadata/CacheKeyExtractor.java new file mode 100644 index 0000000..9017ad7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/metadata/CacheKeyExtractor.java @@ -0,0 +1,7 @@ +package net.infumia.frame.metadata; + +import java.util.function.Function; +import org.bukkit.metadata.Metadatable; + +@FunctionalInterface +public interface CacheKeyExtractor extends Function {} diff --git a/common/src/main/java/net/infumia/frame/metadata/MetadataAccess.java b/common/src/main/java/net/infumia/frame/metadata/MetadataAccess.java new file mode 100644 index 0000000..4eb3652 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/metadata/MetadataAccess.java @@ -0,0 +1,32 @@ +package net.infumia.frame.metadata; + +import java.util.concurrent.Callable; +import net.infumia.frame.typedkey.TypedKey; +import org.bukkit.metadata.LazyMetadataValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface MetadataAccess { + @Nullable + T get(@NotNull TypedKey key); + + @NotNull + T getOrThrow(@NotNull TypedKey key); + + @Nullable + T remove(@NotNull TypedKey key); + + boolean has(@NotNull TypedKey key); + + void setFixed(@NotNull TypedKey key, @NotNull T value); + + void setLazy( + @NotNull TypedKey key, + @NotNull Callable value, + @NotNull LazyMetadataValue.CacheStrategy cacheStrategy + ); + + void setLazy(@NotNull TypedKey key, @NotNull Callable value); + + void removeAll(); +} diff --git a/common/src/main/java/net/infumia/frame/metadata/MetadataAccessFactory.java b/common/src/main/java/net/infumia/frame/metadata/MetadataAccessFactory.java new file mode 100644 index 0000000..26077e9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/metadata/MetadataAccessFactory.java @@ -0,0 +1,12 @@ +package net.infumia.frame.metadata; + +import java.util.Collection; +import org.bukkit.metadata.Metadatable; +import org.jetbrains.annotations.NotNull; + +public interface MetadataAccessFactory { + @NotNull + MetadataAccess getOrCreate(@NotNull Metadatable metadatable); + + void clearCache(@NotNull Collection metadatables); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/Pipeline.java b/common/src/main/java/net/infumia/frame/pipeline/Pipeline.java new file mode 100644 index 0000000..46e8dde --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/Pipeline.java @@ -0,0 +1,4 @@ +package net.infumia.frame.pipeline; + +public interface Pipeline + extends PipelineBase> {} diff --git a/common/src/main/java/net/infumia/frame/pipeline/PipelineBase.java b/common/src/main/java/net/infumia/frame/pipeline/PipelineBase.java new file mode 100644 index 0000000..2f3c699 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/PipelineBase.java @@ -0,0 +1,85 @@ +package net.infumia.frame.pipeline; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.service.Service; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface PipelineBase> { + @NotNull + Self apply(@NotNull Implementation operation); + + @NotNull + CompletableFuture completeWith(@NotNull B context); + + @NotNull + CompletableFuture completeWithAsync(@NotNull B context); + + @NotNull + default Self register(@NotNull final Service service) { + return this.apply(Implementation.register(service)); + } + + @NotNull + default Self register( + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return this.apply(Implementation.register(service, filters)); + } + + @NotNull + default Self replace( + @NotNull final String serviceKey, + @NotNull final UnaryOperator> operation + ) { + return this.apply(Implementation.replace(serviceKey, operation)); + } + + @NotNull + default Self replace( + @NotNull final String serviceKey, + @NotNull final UnaryOperator> operation, + @Nullable final Collection> filters + ) { + return this.apply(Implementation.replace(serviceKey, operation, filters)); + } + + @NotNull + default Self registerAfter( + @NotNull final String serviceKey, + @NotNull final Service service + ) { + return this.apply(Implementation.registerAfter(serviceKey, service)); + } + + @NotNull + default Self registerAfter( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return this.apply(Implementation.registerAfter(serviceKey, service, filters)); + } + + @NotNull + default Self registerBefore( + @NotNull final String serviceKey, + @NotNull final Service service + ) { + return this.apply(Implementation.registerBefore(serviceKey, service)); + } + + @NotNull + default Self registerBefore( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return this.apply(Implementation.registerBefore(serviceKey, service, filters)); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/PipelineConsumer.java b/common/src/main/java/net/infumia/frame/pipeline/PipelineConsumer.java new file mode 100644 index 0000000..f718bfc --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/PipelineConsumer.java @@ -0,0 +1,6 @@ +package net.infumia.frame.pipeline; + +import net.infumia.frame.service.ConsumerService; + +public interface PipelineConsumer + extends PipelineBase> {} diff --git a/common/src/main/java/net/infumia/frame/pipeline/PipelineContext.java b/common/src/main/java/net/infumia/frame/pipeline/PipelineContext.java new file mode 100644 index 0000000..4a779b1 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/PipelineContext.java @@ -0,0 +1,3 @@ +package net.infumia.frame.pipeline; + +public interface PipelineContext {} diff --git a/common/src/main/java/net/infumia/frame/pipeline/PipelineService.java b/common/src/main/java/net/infumia/frame/pipeline/PipelineService.java new file mode 100644 index 0000000..8338307 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/PipelineService.java @@ -0,0 +1,5 @@ +package net.infumia.frame.pipeline; + +import net.infumia.frame.service.Service; + +public interface PipelineService extends Service {} diff --git a/common/src/main/java/net/infumia/frame/pipeline/PipelineServiceConsumer.java b/common/src/main/java/net/infumia/frame/pipeline/PipelineServiceConsumer.java new file mode 100644 index 0000000..ba445db --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/PipelineServiceConsumer.java @@ -0,0 +1,6 @@ +package net.infumia.frame.pipeline; + +import net.infumia.frame.service.ConsumerService; + +public interface PipelineServiceConsumer + extends ConsumerService, PipelineService {} diff --git a/common/src/main/java/net/infumia/frame/pipeline/Pipelined.java b/common/src/main/java/net/infumia/frame/pipeline/Pipelined.java new file mode 100644 index 0000000..11b0ef3 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/Pipelined.java @@ -0,0 +1,8 @@ +package net.infumia.frame.pipeline; + +import org.jetbrains.annotations.NotNull; + +public interface Pipelined

{ + @NotNull + P pipelines(); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElement.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElement.java new file mode 100644 index 0000000..9d0ba31 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElement.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.context; + +import net.infumia.frame.context.element.ContextElementClear; +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.context.element.ContextElementRender; +import net.infumia.frame.context.element.ContextElementUpdate; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.service.Cancellable; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextElement extends PipelineContext { + interface Render extends PipelineContextElement { + @NotNull + ContextElementRender context(); + } + + interface Clear extends PipelineContextElement { + @NotNull + ContextElementClear context(); + } + + interface Update extends PipelineContextElement, Cancellable { + @NotNull + ContextElementUpdate context(); + } + + interface Click extends PipelineContextElement, Cancellable { + @NotNull + ContextElementClick context(); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManager.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManager.java new file mode 100644 index 0000000..5816c8c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManager.java @@ -0,0 +1,29 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import net.infumia.frame.Frame; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.view.View; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextManager extends PipelineContext { + @NotNull + Frame frame(); + + interface ViewCreated extends PipelineContextManager { + @NotNull + Collection> registeredViews(); + } + + interface ViewRegistered extends PipelineContextManager { + @NotNull + Collection registeredViews(); + } + + interface ListenerRegistered extends PipelineContextManager {} + + interface ViewUnregistered extends PipelineContextManager { + @NotNull + Collection unregisteredViews(); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRender.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRender.java new file mode 100644 index 0000000..92dc674 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRender.java @@ -0,0 +1,45 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import java.util.List; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextResume; +import net.infumia.frame.element.Element; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.service.Cancellable; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextRender extends PipelineContext { + @NotNull + ContextRender context(); + + interface FirstRender extends PipelineContextRender { + @NotNull + List elements(); + + void addElement(@NotNull Element element); + } + + interface OpenContainer extends PipelineContextRender { + @NotNull + Collection viewers(); + } + + interface Resume extends PipelineContextRender { + @NotNull + @Override + ContextResume context(); + } + + interface CloseContainer extends PipelineContextRender { + @NotNull + Collection viewers(); + } + + interface StartUpdate extends PipelineContextRender, Cancellable {} + + interface StopUpdate extends PipelineContextRender {} + + interface Update extends PipelineContextRender {} +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextState.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextState.java new file mode 100644 index 0000000..645d560 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextState.java @@ -0,0 +1,29 @@ +package net.infumia.frame.pipeline.context; + +import net.infumia.frame.Frame; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.state.State; +import net.infumia.frame.state.value.StateValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface PipelineContextState extends PipelineContext { + @NotNull + Frame manager(); + + @NotNull + State state(); + + interface Access extends PipelineContextState { + @NotNull + StateValue value(); + } + + interface Update extends PipelineContextState { + @Nullable + Object oldValue(); + + @NotNull + StateValue value(); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextView.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextView.java new file mode 100644 index 0000000..c76f8f9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextView.java @@ -0,0 +1,123 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import java.util.Map; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.service.Cancellable; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextView extends PipelineContext { + interface Init extends PipelineContextView { + @NotNull + View view(); + } + + interface CreateViewers extends PipelineContextView { + @NotNull + View view(); + + @NotNull + Collection viewers(); + } + + interface CreateContext extends PipelineContextView { + @NotNull + View view(); + + @NotNull + Collection viewers(); + + @NotNull + TypedKeyStorageImmutable initialData(); + } + + interface Transition extends PipelineContextView { + @NotNull + ContextBase context(); + + @NotNull + Collection viewers(); + } + + interface Open extends PipelineContextView, Cancellable { + @NotNull + ContextOpen context(); + } + + interface ProcessConfigModifier extends PipelineContextView { + @NotNull + ContextOpen context(); + } + + interface CreateContainer extends PipelineContextView { + @NotNull + ContextBase context(); + + @NotNull + ViewConfig config(); + } + + interface ModifyContainer extends PipelineContextView { + @NotNull + ContextBase context(); + + @NotNull + ViewConfig config(); + + @NotNull + ViewContainer container(); + + void modifyContainer(@NotNull ViewContainer newContainer); + } + + interface LayoutResolution extends PipelineContextView { + @NotNull + ContextBase context(); + + @NotNull + ViewConfig config(); + + @NotNull + ViewContainer container(); + + @NotNull + Map layouts(); + + void addLayout(char character, @NotNull Collection indexes); + } + + interface CreateRender extends PipelineContextView { + @NotNull + ContextBase context(); + + @NotNull + ViewConfig config(); + + @NotNull + ViewContainer container(); + + @NotNull + Map layouts(); + } + + interface Click extends PipelineContextView, Cancellable { + @NotNull + ContextClick context(); + } + + interface Close extends PipelineContextView, Cancellable { + @NotNull + ContextClose context(); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewer.java b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewer.java new file mode 100644 index 0000000..363f4dc --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewer.java @@ -0,0 +1,22 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineContext; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextViewer extends PipelineContext { + @NotNull + ContextRender context(); + + interface Added extends PipelineContextViewer { + @NotNull + Collection viewers(); + } + + interface Removed extends PipelineContextViewer { + @NotNull + Collection viewers(); + } +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElement.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElement.java new file mode 100644 index 0000000..07555b8 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElement.java @@ -0,0 +1,42 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import org.jetbrains.annotations.NotNull; + +public interface PipelineExecutorElement { + @NotNull + CompletableFuture executeRender(@NotNull ContextRender context); + + @NotNull + CompletableFuture executeUpdate( + @NotNull ContextRender context, + boolean forced + ); + + @NotNull + CompletableFuture executeClick(@NotNull ContextClick context); + + @NotNull + CompletableFuture executeClear(@NotNull ContextRender context); + + void applyRender( + @NotNull Implementation implementation + ); + + void applyUpdate( + @NotNull Implementation implementation + ); + + void applyClick( + @NotNull Implementation implementation + ); + + void applyClear( + @NotNull Implementation implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManager.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManager.java new file mode 100644 index 0000000..21cae82 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManager.java @@ -0,0 +1,57 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.view.View; +import org.jetbrains.annotations.NotNull; + +public interface PipelineExecutorManager { + @NotNull + CompletableFuture> executeViewCreated( + @NotNull Collection> registeredViews + ); + + @NotNull + CompletableFuture> executeViewRegistered( + @NotNull Collection registeredViews + ); + + @NotNull + CompletableFuture executeListenersRegistered(); + + @NotNull + CompletableFuture executeViewUnregistered( + @NotNull Collection unregisteredViews + ); + + void applyViewCreated( + @NotNull Implementation< + PipelineContextManager.ViewCreated, + Collection + > implementation + ); + + void applyViewRegistered( + @NotNull Implementation< + PipelineContextManager.ViewRegistered, + Collection + > implementation + ); + + void applyListenersRegistered( + @NotNull Implementation< + PipelineContextManager.ListenerRegistered, + ConsumerService.State + > implementation + ); + + void applyViewUnregistered( + @NotNull Implementation< + PipelineContextManager.ViewUnregistered, + ConsumerService.State + > implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRender.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRender.java new file mode 100644 index 0000000..e87e189 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRender.java @@ -0,0 +1,82 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineExecutorRender { + @NotNull + CompletableFuture executeFirstRender(); + + @NotNull + CompletableFuture executeTransition(@NotNull Collection viewers); + + @NotNull + CompletableFuture executeOpenContainer( + @NotNull Collection viewers + ); + + @NotNull + CompletableFuture executeStartUpdate(); + + @NotNull + CompletableFuture executeResume( + @NotNull ContextRender from, + @NotNull Collection viewers + ); + + @NotNull + CompletableFuture executeStopUpdate(); + + @NotNull + CompletableFuture executeUpdate(); + + void applyFirstRender( + @NotNull Implementation< + PipelineContextRender.FirstRender, + ConsumerService.State + > implementation + ); + + void applyTransition( + @NotNull Implementation< + PipelineContextView.Transition, + ConsumerService.State + > implementation + ); + + void applyOpenContainer( + @NotNull Implementation< + PipelineContextRender.OpenContainer, + ConsumerService.State + > implementation + ); + + void applyStartUpdate( + @NotNull Implementation< + PipelineContextRender.StartUpdate, + ConsumerService.State + > implementation + ); + + void applyResume( + @NotNull Implementation implementation + ); + + void applyStopUpdate( + @NotNull Implementation< + PipelineContextRender.StopUpdate, + ConsumerService.State + > implementation + ); + + void applyUpdate( + @NotNull Implementation implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorState.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorState.java new file mode 100644 index 0000000..9f746e4 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorState.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.context.PipelineContextState; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.state.State; +import net.infumia.frame.state.value.StateValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface PipelineExecutorState { + @NotNull + CompletableFuture executeAccess( + @NotNull State state, + @NotNull StateValue value + ); + + @NotNull + CompletableFuture executeUpdate( + @NotNull State state, + @Nullable Object oldValue, + @NotNull StateValue value + ); + + void applyAccess( + @NotNull Implementation implementation + ); + + void applyUpdate( + @NotNull Implementation implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorView.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorView.java new file mode 100644 index 0000000..9f590d1 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorView.java @@ -0,0 +1,146 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.util.Pair; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.ContextualViewer; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; + +public interface PipelineExecutorView { + @NotNull + CompletableFuture executeInit(@NotNull ContextInit context); + + @NotNull + CompletableFuture> executeCreateViewers(@NotNull Collection viewers); + + @NotNull + CompletableFuture executeCreateContext( + @NotNull Collection viewers, + @NotNull TypedKeyStorageImmutable initialData + ); + + @NotNull + CompletableFuture> executeOpen( + @NotNull ContextBase context + ); + + @NotNull + CompletableFuture executeProcessConfigModifiers( + @NotNull ContextOpen context + ); + + @NotNull + CompletableFuture executeCreateContainer( + @NotNull ContextBase context, + @NotNull ViewConfig config + ); + + @NotNull + CompletableFuture< + Pair + > executeModifyContainer( + @NotNull ContextBase context, + @NotNull ViewConfig config, + @NotNull ViewContainer container + ); + + @NotNull + CompletableFuture< + Pair> + > executeLayoutResolution( + @NotNull ContextBase context, + @NotNull ViewConfig config, + @NotNull ViewContainer container + ); + + @NotNull + CompletableFuture executeCreateRender( + @NotNull ContextBase context, + @NotNull ViewConfig config, + @NotNull ViewContainer container, + @NotNull Map layouts + ); + + @NotNull + CompletableFuture executeClick( + @NotNull ContextualViewer clicker, + @NotNull InventoryClickEvent event + ); + + @NotNull + CompletableFuture executeClose( + @NotNull ContextualViewer viewer, + boolean forced + ); + + void applyInit( + @NotNull Implementation implementation + ); + + void applyCreateViewers( + @NotNull Implementation< + PipelineContextView.CreateViewers, + Collection + > implementation + ); + + void applyCreateContext( + @NotNull Implementation implementation + ); + + void applyOpen( + @NotNull Implementation implementation + ); + + void applyProcessConfigModifiers( + @NotNull Implementation< + PipelineContextView.ProcessConfigModifier, + ConsumerService.State + > implementation + ); + + void applyCreateContainer( + @NotNull Implementation implementation + ); + + void applyModifyContainer( + @NotNull Implementation< + PipelineContextView.ModifyContainer, + ConsumerService.State + > implementation + ); + + void applyLayoutResolution( + @NotNull Implementation< + PipelineContextView.LayoutResolution, + ConsumerService.State + > implementation + ); + + void applyCreateRender( + @NotNull Implementation implementation + ); + + void applyClick( + @NotNull Implementation implementation + ); + + void applyClose( + @NotNull Implementation implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewer.java b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewer.java new file mode 100644 index 0000000..b273c5a --- /dev/null +++ b/common/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewer.java @@ -0,0 +1,25 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineExecutorViewer { + @NotNull + CompletableFuture executeAdded(@NotNull Collection viewers); + + @NotNull + CompletableFuture executeRemoved(@NotNull Collection viewers); + + void applyAdded( + @NotNull Implementation implementation + ); + + void applyRemoved( + @NotNull Implementation implementation + ); +} diff --git a/common/src/main/java/net/infumia/frame/service/Cancellable.java b/common/src/main/java/net/infumia/frame/service/Cancellable.java new file mode 100644 index 0000000..db09196 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/Cancellable.java @@ -0,0 +1,7 @@ +package net.infumia.frame.service; + +public interface Cancellable { + boolean cancelled(); + + void cancelled(boolean cancelled); +} diff --git a/common/src/main/java/net/infumia/frame/service/ConsumerService.java b/common/src/main/java/net/infumia/frame/service/ConsumerService.java new file mode 100644 index 0000000..33e1107 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ConsumerService.java @@ -0,0 +1,42 @@ +package net.infumia.frame.service; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +public interface ConsumerService + extends + Service, + BiConsumer, Context>, + Consumer { + @Override + @ApiStatus.OverrideOnly + default void accept(final Context ctx) {} + + @Override + @ApiStatus.OverrideOnly + default void accept(@NotNull final CompletableFuture future, final Context ctx) { + this.accept(ctx); + future.complete(State.CONTINUE); + } + + @NotNull + @Override + @ApiStatus.NonExtendable + default CompletableFuture handle(final Context ctx) { + final CompletableFuture future = new CompletableFuture<>(); + try { + this.accept(future, ctx); + } catch (final Throwable throwable) { + future.completeExceptionally(throwable); + } + return future; + } + + enum State { + CONTINUE, + FINISHED, + } +} diff --git a/common/src/main/java/net/infumia/frame/service/Implementation.java b/common/src/main/java/net/infumia/frame/service/Implementation.java new file mode 100644 index 0000000..aa4328e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/Implementation.java @@ -0,0 +1,77 @@ +package net.infumia.frame.service; + +import java.util.Collection; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface Implementation { + @NotNull + static Implementation register( + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return new Register<>(service, filters); + } + + @NotNull + static Implementation register( + @NotNull final Service service + ) { + return Implementation.register(service, null); + } + + @NotNull + static Implementation registerBefore( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return new RegisterBefore<>(serviceKey, service, filters); + } + + @NotNull + static Implementation registerBefore( + @NotNull final String serviceKey, + @NotNull final Service service + ) { + return Implementation.registerBefore(serviceKey, service, null); + } + + @NotNull + static Implementation registerAfter( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + return new RegisterAfter<>(serviceKey, service, filters); + } + + @NotNull + static Implementation registerAfter( + @NotNull final String serviceKey, + @NotNull final Service service + ) { + return Implementation.registerAfter(serviceKey, service, null); + } + + @NotNull + static Implementation replace( + @NotNull final String serviceKey, + @NotNull final UnaryOperator> service, + @Nullable final Collection> filters + ) { + return new Replace<>(serviceKey, service, filters); + } + + @NotNull + static Implementation replace( + @NotNull final String serviceKey, + @NotNull final UnaryOperator> service + ) { + return Implementation.replace(serviceKey, service, null); + } + + void handle(@NotNull ServiceRepository repository); +} diff --git a/common/src/main/java/net/infumia/frame/service/Register.java b/common/src/main/java/net/infumia/frame/service/Register.java new file mode 100644 index 0000000..b721bf5 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/Register.java @@ -0,0 +1,27 @@ +package net.infumia.frame.service; + +import java.util.Collection; +import java.util.function.Predicate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class Register implements Implementation { + + private final Service service; + private final Collection> filters; + + Register( + @NotNull final Service service, + @Nullable final Collection> filters + ) { + this.service = service; + this.filters = filters; + } + + @Override + public void handle(@NotNull final ServiceRepository repository) { + repository.implementations.add( + new ServiceWrapper<>(repository.serviceType, this.service, false, this.filters) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/RegisterAfter.java b/common/src/main/java/net/infumia/frame/service/RegisterAfter.java new file mode 100644 index 0000000..30f33f7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/RegisterAfter.java @@ -0,0 +1,58 @@ +package net.infumia.frame.service; + +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import net.infumia.frame.util.Keyed; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class RegisterAfter implements Implementation { + + private final String serviceKey; + private final Service service; + private final Collection> filters; + + RegisterAfter( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + this.serviceKey = serviceKey; + this.service = service; + this.filters = filters; + } + + @Override + public void handle(@NotNull final ServiceRepository repository) { + final List> implementations = repository.implementations; + for (int index = 0; index < implementations.size(); index++) { + final ServiceWrapper wrapper = implementations.get(index); + final Service implementation = wrapper.implementation; + if (implementation.key().equals(this.serviceKey)) { + Preconditions.argument( + !wrapper.defaultImplementation, + "You cannot put an implementation before the default implementation! Try to replace it instead." + ); + implementations.add( + index, + new ServiceWrapper<>(repository.serviceType, this.service, false, this.filters) + ); + return; + } + } + throw new IllegalArgumentException( + String.format( + "Service '%s' not found in the implementation list '%s'!", + this.serviceKey, + implementations + .stream() + .map(wrapper -> wrapper.implementation) + .map(Keyed::key) + .collect(Collectors.toSet()) + ) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/RegisterBefore.java b/common/src/main/java/net/infumia/frame/service/RegisterBefore.java new file mode 100644 index 0000000..4bfa40f --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/RegisterBefore.java @@ -0,0 +1,48 @@ +package net.infumia.frame.service; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Predicate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class RegisterBefore implements Implementation { + + private final String serviceKey; + private final Service service; + private final Collection> filters; + + RegisterBefore( + @NotNull final String serviceKey, + @NotNull final Service service, + @Nullable final Collection> filters + ) { + this.serviceKey = serviceKey; + this.service = service; + this.filters = filters; + } + + @Override + public void handle(@NotNull final ServiceRepository repository) { + final List> implementations = repository.implementations; + final ListIterator> iterator = + implementations.listIterator(); + while (iterator.hasNext()) { + final ServiceWrapper next = iterator.next(); + if (next.implementation.key().equals(this.serviceKey)) { + iterator.add( + new ServiceWrapper<>(repository.serviceType, this.service, false, this.filters) + ); + return; + } + } + throw new IllegalArgumentException( + String.format( + "Service '%s' not found in the implementation list '%s'!", + this.serviceKey, + implementations + ) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/Replace.java b/common/src/main/java/net/infumia/frame/service/Replace.java new file mode 100644 index 0000000..a935347 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/Replace.java @@ -0,0 +1,60 @@ +package net.infumia.frame.service; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import net.infumia.frame.util.Keyed; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class Replace implements Implementation { + + private final String serviceKey; + private final UnaryOperator> service; + private final Collection> filters; + + Replace( + @NotNull final String serviceKey, + @NotNull final UnaryOperator> service, + @Nullable final Collection> filters + ) { + this.serviceKey = serviceKey; + this.service = service; + this.filters = filters; + } + + @Override + public void handle(@NotNull final ServiceRepository repository) { + final List> implementations = repository.implementations; + final ListIterator> iterator = + implementations.listIterator(); + while (iterator.hasNext()) { + final Service oldImplementation = iterator.next().implementation; + if (oldImplementation.key().equals(this.serviceKey)) { + iterator.set( + new ServiceWrapper<>( + repository.serviceType, + this.service.apply(oldImplementation), + false, + this.filters + ) + ); + return; + } + } + throw new IllegalArgumentException( + String.format( + "Service '%s' not found in the implementation list '%s'!", + this.serviceKey, + implementations + .stream() + .map(wrapper -> wrapper.implementation) + .map(Keyed::key) + .collect(Collectors.toSet()) + ) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/Service.java b/common/src/main/java/net/infumia/frame/service/Service.java new file mode 100644 index 0000000..de3c530 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/Service.java @@ -0,0 +1,10 @@ +package net.infumia.frame.service; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.util.Keyed; +import org.jetbrains.annotations.NotNull; + +public interface Service extends Keyed { + @NotNull + CompletableFuture handle(Context context); +} diff --git a/common/src/main/java/net/infumia/frame/service/ServicePipeline.java b/common/src/main/java/net/infumia/frame/service/ServicePipeline.java new file mode 100644 index 0000000..98f23a5 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ServicePipeline.java @@ -0,0 +1,32 @@ +package net.infumia.frame.service; + +import io.leangen.geantyref.TypeToken; +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; +import org.jetbrains.annotations.NotNull; + +public final class ServicePipeline { + + final Executor executor; + final Duration timeout; + final ScheduledExecutorService delayer; + + ServicePipeline( + @NotNull final Executor executor, + @NotNull final Duration timeout, + @NotNull final ScheduledExecutorService delayer + ) { + this.executor = executor; + this.timeout = timeout; + this.delayer = delayer; + } + + @NotNull + public ServiceRepository create( + @NotNull final TypeToken> type, + @NotNull final Service defaultImplementation + ) { + return new ServiceRepository<>(this, type, defaultImplementation); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/ServicePipelineBuilder.java b/common/src/main/java/net/infumia/frame/service/ServicePipelineBuilder.java new file mode 100644 index 0000000..607ca58 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ServicePipelineBuilder.java @@ -0,0 +1,44 @@ +package net.infumia.frame.service; + +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import org.jetbrains.annotations.NotNull; + +public final class ServicePipelineBuilder { + + private Executor executor = Executors.newSingleThreadExecutor(); + private Duration timeout = Duration.ofSeconds(10L); + private ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1); + + @NotNull + public static ServicePipelineBuilder newBuilder() { + return new ServicePipelineBuilder(); + } + + @NotNull + public ServicePipelineBuilder executor(@NotNull final Executor executor) { + this.executor = executor; + return this; + } + + @NotNull + public ServicePipelineBuilder timeout(@NotNull final Duration timeout) { + this.timeout = timeout; + return this; + } + + @NotNull + public ServicePipelineBuilder delayer(@NotNull final ScheduledExecutorService delayer) { + this.delayer = delayer; + return this; + } + + @NotNull + public ServicePipeline build() { + return new ServicePipeline(this.executor, this.timeout, this.delayer); + } + + private ServicePipelineBuilder() {} +} diff --git a/common/src/main/java/net/infumia/frame/service/ServiceRepository.java b/common/src/main/java/net/infumia/frame/service/ServiceRepository.java new file mode 100644 index 0000000..73b4450 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ServiceRepository.java @@ -0,0 +1,53 @@ +package net.infumia.frame.service; + +import io.leangen.geantyref.TypeToken; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRepository { + + private final ServicePipeline pipeline; + final List> implementations; + final TypeToken> serviceType; + + public ServiceRepository( + @NotNull final ServicePipeline pipeline, + @NotNull final TypeToken> serviceType, + @NotNull final Service defaultImplementation + ) { + this.pipeline = pipeline; + this.serviceType = serviceType; + this.implementations = new LinkedList<>(); + this.implementations.add( + new ServiceWrapper<>(serviceType, defaultImplementation, true, null) + ); + } + + public void apply(@NotNull final Implementation operation) { + synchronized (this.implementations) { + operation.handle(this); + } + } + + @NotNull + public CompletableFuture completeWith(@NotNull final Context context) { + return new ServiceSpigot<>(this.pipeline, this, context).complete(); + } + + @NotNull + public CompletableFuture completeWithAsync(@NotNull final Context context) { + return new ServiceSpigot<>(this.pipeline, this, context).completeAsync(); + } + + @NotNull + LinkedList> queue() { + synchronized (this.implementations) { + return this.implementations.stream() + .sorted() + .collect(Collectors.toCollection(LinkedList::new)); + } + } +} diff --git a/common/src/main/java/net/infumia/frame/service/ServiceSpigot.java b/common/src/main/java/net/infumia/frame/service/ServiceSpigot.java new file mode 100644 index 0000000..ffe28a3 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ServiceSpigot.java @@ -0,0 +1,145 @@ +package net.infumia.frame.service; + +import java.util.LinkedList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class ServiceSpigot { + + private final ServicePipeline pipeline; + private final ServiceRepository repository; + private final Context context; + + ServiceSpigot( + @NotNull final ServicePipeline pipeline, + @NotNull final ServiceRepository repository, + @NotNull final Context context + ) { + this.pipeline = pipeline; + this.repository = repository; + this.context = context; + } + + @NotNull + public CompletableFuture complete() { + return this.completeInternally(Runnable::run); + } + + @NotNull + public CompletableFuture completeAsync() { + return this.completeInternally(this.pipeline.executor); + } + + @NotNull + private CompletableFuture completeInternally(@NotNull final Executor executor) { + final CompletableFuture<@Nullable Result> future = new CompletableFuture<>(); + final ScheduledFuture delayer = this.scheduleTimeout(future); + final AtomicBoolean isConsumerService = new AtomicBoolean(false); + + executor.execute(() -> + this.processServices(isConsumerService).whenComplete((result, throwable) -> { + if (delayer.cancel(true)) { + if (throwable == null) { + future.complete(result); + } else { + future.completeExceptionally(throwable); + } + } + }) + ); + + return future.thenApply(result -> this.checkFinalResult(isConsumerService, result)); + } + + @NotNull + private CompletableFuture<@Nullable Result> processServices( + @NotNull final AtomicBoolean isConsumerService + ) { + CompletableFuture<@Nullable Result> job = CompletableFuture.completedFuture(null); + final LinkedList> queue = this.repository.queue(); + ServiceWrapper wrapper; + + while ((wrapper = queue.pollLast()) != null) { + job = this.processService(isConsumerService, wrapper, job); + } + return job; + } + + @NotNull + private CompletableFuture<@Nullable Result> processService( + @NotNull final AtomicBoolean isConsumerService, + @NotNull final ServiceWrapper wrapper, + @NotNull final CompletableFuture<@Nullable Result> job + ) { + final Service service = wrapper.implementation; + isConsumerService.set(service instanceof ConsumerService); + if (!wrapper.passes(this.context)) { + return job; + } + return job.thenCompose(result -> + this.shouldContinue(isConsumerService, result) + ? service.handle(this.context) + : CompletableFuture.completedFuture(result) + ); + } + + private boolean shouldContinue( + @NotNull final AtomicBoolean isConsumerService, + @Nullable final Result result + ) { + if (this.isCancelled()) { + return false; + } + if (result == null) { + return true; + } + return isConsumerService.get() && result != ConsumerService.State.FINISHED; + } + + @NotNull + @SuppressWarnings("unchecked") + private Result checkFinalResult( + @NotNull final AtomicBoolean isConsumerService, + @Nullable final Result result + ) { + if (isConsumerService.get()) { + return (Result) ConsumerService.State.FINISHED; + } + return Preconditions.argumentNotNull(result, "No service consumed the context."); + } + + private boolean isCancelled() { + return this.context instanceof Cancellable && ((Cancellable) this.context).cancelled(); + } + + @NotNull + private ScheduledFuture scheduleTimeout(@NotNull final CompletableFuture future) { + return this.pipeline.delayer.schedule( + () -> this.tryTimeout(future), + this.pipeline.timeout.toMillis(), + TimeUnit.MILLISECONDS + ); + } + + private void tryTimeout(@NotNull final CompletableFuture future) { + if (future.isDone()) { + return; + } + future.completeExceptionally( + new TimeoutException( + String.format( + "Service '%s' could not complete in time %sms", + this.repository.serviceType.getType().getTypeName(), + this.pipeline.timeout.toMillis() + ) + ) + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/ServiceWrapper.java b/common/src/main/java/net/infumia/frame/service/ServiceWrapper.java new file mode 100644 index 0000000..ad94913 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/ServiceWrapper.java @@ -0,0 +1,65 @@ +package net.infumia.frame.service; + +import io.leangen.geantyref.TypeToken; +import java.util.Collection; +import java.util.function.Predicate; +import net.infumia.frame.service.exception.PipelineException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class ServiceWrapper implements Comparable> { + + private final TypeToken> serviceType; + private final Collection> filters; + final Service implementation; + final boolean defaultImplementation; + + ServiceWrapper( + @NotNull final TypeToken> serviceType, + @NotNull final Service implementation, + final boolean defaultImplementation, + @Nullable final Collection> filters + ) { + this.serviceType = serviceType; + this.implementation = implementation; + this.defaultImplementation = defaultImplementation; + this.filters = filters; + } + + boolean passes(@NotNull final Context context) { + if (this.defaultImplementation || this.filters == null) { + return true; + } + for (final Predicate predicate : this.filters) { + try { + if (!predicate.test(context)) { + return false; + } + } catch (final Exception e) { + throw new PipelineException( + String.format( + "Failed to evaluate filter '%s' for '%s'", + TypeToken.get(predicate.getClass()).getType().getTypeName(), + this + ), + e + ); + } + } + return true; + } + + @Override + public int compareTo(@NotNull final ServiceWrapper o) { + return Boolean.compare(o.defaultImplementation, this.defaultImplementation); + } + + @Override + public String toString() { + return String.format( + "ServiceWrapper{type=%s,implementation=%s}", + this.serviceType.getType().getTypeName(), + this.implementation + ); + } +} diff --git a/common/src/main/java/net/infumia/frame/service/exception/PipelineException.java b/common/src/main/java/net/infumia/frame/service/exception/PipelineException.java new file mode 100644 index 0000000..32bd8d9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/service/exception/PipelineException.java @@ -0,0 +1,10 @@ +package net.infumia.frame.service.exception; + +import org.jetbrains.annotations.NotNull; + +public final class PipelineException extends RuntimeException { + + public PipelineException(@NotNull final String message, @NotNull final Throwable cause) { + super(message, cause); + } +} diff --git a/common/src/main/java/net/infumia/frame/slot/LayoutSlot.java b/common/src/main/java/net/infumia/frame/slot/LayoutSlot.java new file mode 100644 index 0000000..218f749 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/slot/LayoutSlot.java @@ -0,0 +1,19 @@ +package net.infumia.frame.slot; + +import java.util.function.IntFunction; +import net.infumia.frame.element.ElementItemBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface LayoutSlot { + char character(); + + int@NotNull[] slots(); + + @Nullable + IntFunction builderFactory(); + + void builderFactory(@Nullable IntFunction builderFactory); + + boolean contains(int slot); +} diff --git a/common/src/main/java/net/infumia/frame/state/State.java b/common/src/main/java/net/infumia/frame/state/State.java new file mode 100644 index 0000000..d7dc5f2 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/State.java @@ -0,0 +1,26 @@ +package net.infumia.frame.state; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.state.value.StateValueHostHolder; +import net.infumia.frame.state.watcher.StateWatcherAccess; +import net.infumia.frame.state.watcher.StateWatcherUpdate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface State { + @Nullable + T get(@NotNull StateValueHostHolder host); + + @NotNull + T getOtThrow(@NotNull StateValueHostHolder host); + + @NotNull + CompletableFuture<@Nullable T> getWait(@NotNull StateValueHostHolder host); + + @NotNull + CompletableFuture getOtThrowWait(@NotNull StateValueHostHolder host); + + void watchAccess(@NotNull StateValueHostHolder host, @NotNull StateWatcherAccess watcher); + + void watchUpdate(@NotNull StateValueHostHolder host, @NotNull StateWatcherUpdate watcher); +} diff --git a/common/src/main/java/net/infumia/frame/state/StateFactory.java b/common/src/main/java/net/infumia/frame/state/StateFactory.java new file mode 100644 index 0000000..ca69958 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/StateFactory.java @@ -0,0 +1,129 @@ +package net.infumia.frame.state; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.element.pagination.ElementPaginationBuilder; +import net.infumia.frame.state.pagination.ElementConfigurer; +import net.infumia.frame.state.pagination.StatePagination; +import net.infumia.frame.typedkey.TypedKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface StateFactory { + @NotNull + StateInitial createInitialState(@NotNull TypedKey stateKey); + + @NotNull + State createState(@NotNull T initialValue); + + @NotNull + StateMutable createMutableState(@Nullable T initialValue); + + @NotNull + State createComputedState(@NotNull Function computation); + + @NotNull + State createComputedState(@NotNull Supplier computation); + + @NotNull + State createLazyState(@NotNull Function computation); + + @NotNull + State createLazyState(@NotNull Supplier computation); + + @NotNull + StatePagination createPaginationState( + @NotNull List source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createComputedPaginationState( + @NotNull Supplier> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createComputedPaginationState( + @NotNull Function> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createComputedAsyncPaginationState( + @NotNull Supplier>> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createComputedAsyncPaginationState( + @NotNull Function>> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createLazyPaginationState( + @NotNull Supplier> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createLazyPaginationState( + @NotNull Function> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createLazyAsyncPaginationState( + @NotNull Supplier>> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + StatePagination createLazyAsyncPaginationState( + @NotNull Function>> source, + @NotNull ElementConfigurer configurer + ); + + @NotNull + ElementPaginationBuilder buildPaginationState(@NotNull List source); + + @NotNull + ElementPaginationBuilder buildComputedPaginationState(@NotNull Supplier> source); + + @NotNull + ElementPaginationBuilder buildComputedPaginationState( + @NotNull Function> source + ); + + @NotNull + ElementPaginationBuilder buildComputedAsyncPaginationState( + @NotNull Supplier>> source + ); + + @NotNull + ElementPaginationBuilder buildComputedAsyncPaginationState( + @NotNull Function>> source + ); + + @NotNull + ElementPaginationBuilder buildLazyPaginationState(@NotNull Supplier> source); + + @NotNull + ElementPaginationBuilder buildLazyPaginationState( + @NotNull Function> source + ); + + @NotNull + ElementPaginationBuilder buildLazyAsyncPaginationState( + @NotNull Supplier>> source + ); + + @NotNull + ElementPaginationBuilder buildLazyAsyncPaginationState( + @NotNull Function>> source + ); +} diff --git a/common/src/main/java/net/infumia/frame/state/StateInitial.java b/common/src/main/java/net/infumia/frame/state/StateInitial.java new file mode 100644 index 0000000..85d746c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/StateInitial.java @@ -0,0 +1,5 @@ +package net.infumia.frame.state; + +import net.infumia.frame.util.Keyed; + +public interface StateInitial extends State, Keyed {} diff --git a/common/src/main/java/net/infumia/frame/state/StateMutable.java b/common/src/main/java/net/infumia/frame/state/StateMutable.java new file mode 100644 index 0000000..59b774e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/StateMutable.java @@ -0,0 +1,18 @@ +package net.infumia.frame.state; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.state.value.StateValue; +import net.infumia.frame.state.value.StateValueHostHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface StateMutable extends State { + @Nullable + StateValue set(@NotNull StateValueHostHolder host, @Nullable T value); + + @NotNull + CompletableFuture<@Nullable StateValue> setWait( + @NotNull StateValueHostHolder host, + @Nullable T value + ); +} diff --git a/common/src/main/java/net/infumia/frame/state/pagination/ElementConfigurer.java b/common/src/main/java/net/infumia/frame/state/pagination/ElementConfigurer.java new file mode 100644 index 0000000..0474c9e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/pagination/ElementConfigurer.java @@ -0,0 +1,16 @@ +package net.infumia.frame.state.pagination; + +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.element.ElementItemBuilder; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface ElementConfigurer { + void configure( + @NotNull ContextBase context, + @NotNull ElementItemBuilder builder, + int index, + int slot, + @NotNull T value + ); +} diff --git a/common/src/main/java/net/infumia/frame/state/pagination/StatePagination.java b/common/src/main/java/net/infumia/frame/state/pagination/StatePagination.java new file mode 100644 index 0000000..f3a0898 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/pagination/StatePagination.java @@ -0,0 +1,6 @@ +package net.infumia.frame.state.pagination; + +import net.infumia.frame.element.pagination.ElementPagination; +import net.infumia.frame.state.State; + +public interface StatePagination extends State {} diff --git a/common/src/main/java/net/infumia/frame/state/value/StateUpdate.java b/common/src/main/java/net/infumia/frame/state/value/StateUpdate.java new file mode 100644 index 0000000..d707020 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/value/StateUpdate.java @@ -0,0 +1,42 @@ +package net.infumia.frame.state.value; + +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateUpdate { + + @NotNull + private final State state; + + @Nullable + private final T oldValue; + + @NotNull + private final StateValue value; + + public StateUpdate( + @NotNull final State state, + @Nullable final T oldValue, + @NotNull final StateValue value + ) { + this.state = state; + this.oldValue = oldValue; + this.value = value; + } + + @NotNull + public State state() { + return this.state; + } + + @Nullable + public T oldValue() { + return this.oldValue; + } + + @NotNull + public StateValue value() { + return this.value; + } +} diff --git a/common/src/main/java/net/infumia/frame/state/value/StateValue.java b/common/src/main/java/net/infumia/frame/state/value/StateValue.java new file mode 100644 index 0000000..fb2b9df --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/value/StateValue.java @@ -0,0 +1,12 @@ +package net.infumia.frame.state.value; + +import org.jetbrains.annotations.Nullable; + +public interface StateValue { + @Nullable + T value(); + + void value(@Nullable T value); + + boolean mutable(); +} diff --git a/common/src/main/java/net/infumia/frame/state/value/StateValueHost.java b/common/src/main/java/net/infumia/frame/state/value/StateValueHost.java new file mode 100644 index 0000000..b442a6c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/value/StateValueHost.java @@ -0,0 +1,3 @@ +package net.infumia.frame.state.value; + +public interface StateValueHost {} diff --git a/common/src/main/java/net/infumia/frame/state/value/StateValueHostHolder.java b/common/src/main/java/net/infumia/frame/state/value/StateValueHostHolder.java new file mode 100644 index 0000000..fdeff29 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/value/StateValueHostHolder.java @@ -0,0 +1,8 @@ +package net.infumia.frame.state.value; + +import org.jetbrains.annotations.NotNull; + +public interface StateValueHostHolder { + @NotNull + StateValueHost stateValueHost(); +} diff --git a/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherAccess.java b/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherAccess.java new file mode 100644 index 0000000..ec8a05c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherAccess.java @@ -0,0 +1,9 @@ +package net.infumia.frame.state.watcher; + +import net.infumia.frame.state.value.StateValue; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface StateWatcherAccess { + void access(@NotNull StateValue value); +} diff --git a/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherUpdate.java b/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherUpdate.java new file mode 100644 index 0000000..774ba9c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/state/watcher/StateWatcherUpdate.java @@ -0,0 +1,9 @@ +package net.infumia.frame.state.watcher; + +import net.infumia.frame.state.value.StateUpdate; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface StateWatcherUpdate { + void update(@NotNull StateUpdate update); +} diff --git a/common/src/main/java/net/infumia/frame/task/TaskFactory.java b/common/src/main/java/net/infumia/frame/task/TaskFactory.java new file mode 100644 index 0000000..c60589d --- /dev/null +++ b/common/src/main/java/net/infumia/frame/task/TaskFactory.java @@ -0,0 +1,18 @@ +package net.infumia.frame.task; + +import java.io.Closeable; +import java.time.Duration; +import net.infumia.frame.util.RunnableThrowable; +import org.jetbrains.annotations.NotNull; + +public interface TaskFactory { + @NotNull + Closeable sync(@NotNull RunnableThrowable task); + + @NotNull + Closeable sync( + @NotNull RunnableThrowable task, + @NotNull Duration delay, + @NotNull Duration period + ); +} diff --git a/common/src/main/java/net/infumia/frame/type/InvType.java b/common/src/main/java/net/infumia/frame/type/InvType.java new file mode 100644 index 0000000..972ff9f --- /dev/null +++ b/common/src/main/java/net/infumia/frame/type/InvType.java @@ -0,0 +1,23 @@ +package net.infumia.frame.type; + +import org.jetbrains.annotations.NotNull; + +public enum InvType { + CHEST, + PLAYER, + DISPENSER, + DROPPER, + FURNACE, + HOPPER, + BLAST_FURNACE, + WORKBENCH, + BREWING_STAND, + BEACON, + ANVIL, + SHULKER_BOX, + SMOKER, + MERCHANT; + + @NotNull + public static final InvType@NotNull[] VALUES = InvType.values(); +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKey.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKey.java new file mode 100644 index 0000000..056c928 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKey.java @@ -0,0 +1,58 @@ +package net.infumia.frame.typedkey; + +import io.leangen.geantyref.TypeToken; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public final class TypedKey { + + private final TypeToken cls; + private final String key; + + private TypedKey(@NotNull final TypeToken cls, @NotNull final String key) { + this.cls = cls; + this.key = key; + } + + @NotNull + public static TypedKey of(@NotNull final TypeToken type, @NotNull final String key) { + return new TypedKey<>(type, key); + } + + @NotNull + public static TypedKey of(@NotNull final Class cls, @NotNull final String key) { + return TypedKey.of(TypeToken.get(cls), key); + } + + @NotNull + public TypeToken cls() { + return this.cls; + } + + @NotNull + public String key() { + return this.key; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.key); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final TypedKey key1 = (TypedKey) o; + return Objects.equals(this.key, key1.key); + } + + @Override + public String toString() { + return "TypedKey{" + "cls=" + this.cls + ", key='" + this.key + '\'' + '}'; + } +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorage.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorage.java new file mode 100644 index 0000000..0cc988a --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorage.java @@ -0,0 +1,21 @@ +package net.infumia.frame.typedkey; + +import java.util.function.Function; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface TypedKeyStorage extends TypedKeyStorageImmutable { + boolean put(@NotNull TypedKey key, @NotNull T value); + + @NotNull + T compute(@NotNull TypedKey key, @NotNull Function<@Nullable T, T> oldValueToNewValue); + + @NotNull + T computeIfAbsent(@NotNull TypedKey key, @NotNull Supplier valueSupplier); + + @Nullable + T computeIfPresent(@NotNull TypedKey key, @NotNull Function oldValueToNewValue); + + boolean remove(@NotNull TypedKey key); +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactory.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactory.java new file mode 100644 index 0000000..69fb4b7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactory.java @@ -0,0 +1,23 @@ +package net.infumia.frame.typedkey; + +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +public interface TypedKeyStorageFactory { + @NotNull + static TypedKeyStorageFactory create() { + return new TypedKeyStorageFactoryImpl(); + } + + @NotNull + TypedKeyStorage create(@NotNull Map, Object> base); + + @NotNull + TypedKeyStorageImmutableBuilder createImmutableBuilder(); + + @NotNull + TypedKeyStorageImmutableBuilder createImmutableBuilder(@NotNull Map, Object> base); + + @NotNull + TypedKeyStorageImmutable createImmutableEmpty(); +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactoryImpl.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactoryImpl.java new file mode 100644 index 0000000..8c965f7 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageFactoryImpl.java @@ -0,0 +1,36 @@ +package net.infumia.frame.typedkey; + +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +final class TypedKeyStorageFactoryImpl implements TypedKeyStorageFactory { + + TypedKeyStorageFactoryImpl() {} + + @NotNull + @Override + public TypedKeyStorage create(@NotNull final Map, Object> base) { + return new TypedKeyStorageImpl(base); + } + + @NotNull + @Override + public TypedKeyStorageImmutableBuilder createImmutableBuilder() { + return this.createImmutableBuilder(new HashMap<>()); + } + + @NotNull + @Override + public TypedKeyStorageImmutableBuilder createImmutableBuilder( + @NotNull final Map, Object> base + ) { + return new TypedKeyStorageImmutableBuilderImpl(base); + } + + @NotNull + @Override + public TypedKeyStorageImmutable createImmutableEmpty() { + return TypedKeyStorageImmutableEmpty.INSTANCE; + } +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutable.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutable.java new file mode 100644 index 0000000..33bc08a --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutable.java @@ -0,0 +1,29 @@ +package net.infumia.frame.typedkey; + +import java.util.Collection; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +public interface TypedKeyStorageImmutable { + @Nullable + T get(@NotNull TypedKey key); + + @Nullable + Object get(@NotNull String key); + + boolean contains(@NotNull TypedKey key); + + @NotNull + @UnmodifiableView + Collection> keys(); + + @NotNull + @UnmodifiableView + Collection values(); + + @NotNull + @UnmodifiableView + Collection, Object>> entries(); +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilder.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilder.java new file mode 100644 index 0000000..e7a3084 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilder.java @@ -0,0 +1,14 @@ +package net.infumia.frame.typedkey; + +import org.jetbrains.annotations.NotNull; + +public interface TypedKeyStorageImmutableBuilder { + @NotNull + TypedKeyStorageImmutableBuilder add(@NotNull TypedKey key, @NotNull T value); + + @NotNull + TypedKeyStorageImmutableBuilder remove(@NotNull TypedKey key); + + @NotNull + TypedKeyStorageImmutable build(); +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilderImpl.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilderImpl.java new file mode 100644 index 0000000..24e1a35 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableBuilderImpl.java @@ -0,0 +1,38 @@ +package net.infumia.frame.typedkey; + +import java.util.Collections; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +final class TypedKeyStorageImmutableBuilderImpl implements TypedKeyStorageImmutableBuilder { + + @NotNull + private final Map, Object> map; + + TypedKeyStorageImmutableBuilderImpl(@NotNull final Map, Object> map) { + this.map = map; + } + + @NotNull + @Override + public TypedKeyStorageImmutableBuilder add( + @NotNull final TypedKey key, + @NotNull final T value + ) { + this.map.put(key, value); + return this; + } + + @NotNull + @Override + public TypedKeyStorageImmutableBuilder remove(@NotNull final TypedKey key) { + this.map.remove(key); + return this; + } + + @NotNull + @Override + public TypedKeyStorageImmutable build() { + return new TypedKeyStorageImmutableImpl(Collections.unmodifiableMap(this.map)); + } +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableEmpty.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableEmpty.java new file mode 100644 index 0000000..3502050 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableEmpty.java @@ -0,0 +1,58 @@ +package net.infumia.frame.typedkey; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +final class TypedKeyStorageImmutableEmpty implements TypedKeyStorageImmutable { + + static final TypedKeyStorageImmutable INSTANCE = new TypedKeyStorageImmutableEmpty(); + + private TypedKeyStorageImmutableEmpty() {} + + @Nullable + @Override + public T get(@NotNull final TypedKey key) { + return null; + } + + @Nullable + @Override + public Object get(@NotNull final String key) { + return null; + } + + @Override + public boolean contains(@NotNull final TypedKey key) { + return false; + } + + @NotNull + @Override + @UnmodifiableView + public Collection> keys() { + return Collections.emptySet(); + } + + @NotNull + @Override + @UnmodifiableView + public Collection values() { + return Collections.emptySet(); + } + + @NotNull + @Override + @UnmodifiableView + public Collection, Object>> entries() { + return Collections.emptySet(); + } + + @Override + public String toString() { + return "TypedKeyStorageImmutableEmpty{}"; + } +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableImpl.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableImpl.java new file mode 100644 index 0000000..0ed759b --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImmutableImpl.java @@ -0,0 +1,63 @@ +package net.infumia.frame.typedkey; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +class TypedKeyStorageImmutableImpl implements TypedKeyStorageImmutable { + + @NotNull + protected final Map, Object> map; + + TypedKeyStorageImmutableImpl(@NotNull final Map, Object> map) { + this.map = map; + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T get(@NotNull final TypedKey key) { + return (T) this.map.get(key); + } + + @Nullable + @Override + public Object get(@NotNull final String key) { + return this.map.keySet() + .stream() + .filter(k -> k.key().equals(key)) + .findFirst() + .map(this::get) + .orElse(null); + } + + @Override + public boolean contains(@NotNull final TypedKey key) { + return this.map.containsKey(key); + } + + @NotNull + @Override + public Collection> keys() { + return Collections.unmodifiableCollection(this.map.keySet()); + } + + @NotNull + @Override + public Collection values() { + return Collections.unmodifiableCollection(this.map.values()); + } + + @NotNull + @Override + public Collection, Object>> entries() { + return Collections.unmodifiableCollection(this.map.entrySet()); + } + + @Override + public String toString() { + return "TypedKeyStorageImmutableImpl{" + "map=" + this.map + '}'; + } +} diff --git a/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImpl.java b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImpl.java new file mode 100644 index 0000000..2f678bb --- /dev/null +++ b/common/src/main/java/net/infumia/frame/typedkey/TypedKeyStorageImpl.java @@ -0,0 +1,67 @@ +package net.infumia.frame.typedkey; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class TypedKeyStorageImpl extends TypedKeyStorageImmutableImpl implements TypedKeyStorage { + + TypedKeyStorageImpl(@NotNull final Map, Object> map) { + super(map); + } + + @Override + public boolean put(@NotNull final TypedKey key, @NotNull final T value) { + return this.map.put(key, value) == null; + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public T compute( + @NotNull final TypedKey key, + @NotNull final Function<@Nullable T, T> oldValueToNewValue + ) { + return (T) this.map.compute(key, (__, oldValue) -> { + if (oldValue == null) { + return oldValueToNewValue.apply(null); + } else { + return oldValueToNewValue.apply((T) oldValue); + } + }); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public T computeIfAbsent( + @NotNull final TypedKey key, + @NotNull final Supplier valueSupplier + ) { + return (T) this.map.computeIfAbsent(key, __ -> valueSupplier.get()); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T computeIfPresent( + @NotNull final TypedKey key, + @NotNull final Function oldValueToNewValue + ) { + return (T) this.map.computeIfPresent(key, (__, oldValue) -> + oldValueToNewValue.apply((T) oldValue) + ); + } + + @Override + public boolean remove(@NotNull final TypedKey key) { + return this.map.remove(key) != null; + } + + @Override + public String toString() { + return "TypedKeyStorageImpl{" + "map=" + this.map + '}'; + } +} diff --git a/common/src/main/java/net/infumia/frame/util/Cache.java b/common/src/main/java/net/infumia/frame/util/Cache.java new file mode 100644 index 0000000..1ea35fd --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Cache.java @@ -0,0 +1,49 @@ +package net.infumia.frame.util; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class Cache implements Supplier { + + @NotNull + private final Supplier supplier; + + @Nullable + private volatile T value; + + private Cache(@NotNull final Supplier supplier) { + this.supplier = supplier; + } + + @NotNull + public static Cache of(@NotNull final Supplier supplier) { + return new Cache<>(Objects.requireNonNull(supplier, "supplier")); + } + + @Override + public T get() { + T val = this.value; + if (val == null) { + synchronized (this) { + val = this.value; + if (val == null) { + val = this.supplier.get(); + this.value = val; + } + } + } + return val; + } + + @NotNull + public Optional ifPresent() { + return Optional.ofNullable(this.value); + } + + public void invalidate() { + this.value = null; + } +} diff --git a/common/src/main/java/net/infumia/frame/util/Keyed.java b/common/src/main/java/net/infumia/frame/util/Keyed.java new file mode 100644 index 0000000..c22cc40 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Keyed.java @@ -0,0 +1,5 @@ +package net.infumia.frame.util; + +public interface Keyed { + T key(); +} diff --git a/common/src/main/java/net/infumia/frame/util/Lazy.java b/common/src/main/java/net/infumia/frame/util/Lazy.java new file mode 100644 index 0000000..498884d --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Lazy.java @@ -0,0 +1,24 @@ +package net.infumia.frame.util; + +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +public final class Lazy implements Supplier { + + @NotNull + private final Cache delegate; + + private Lazy(@NotNull final Cache delegate) { + this.delegate = delegate; + } + + @NotNull + public static Lazy of(@NotNull final Supplier supplier) { + return new Lazy<>(Cache.of(supplier)); + } + + @Override + public T get() { + return this.delegate.get(); + } +} diff --git a/common/src/main/java/net/infumia/frame/util/Pair.java b/common/src/main/java/net/infumia/frame/util/Pair.java new file mode 100644 index 0000000..173dbcf --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Pair.java @@ -0,0 +1,45 @@ +package net.infumia.frame.util; + +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +public final class Pair { + + @NotNull + private final F first; + + @NotNull + private final S second; + + private Pair(@NotNull final F first, @NotNull final S second) { + this.first = first; + this.second = second; + } + + @NotNull + public static Pair of(@NotNull final F first, @NotNull final S second) { + return new Pair<>(first, second); + } + + @NotNull + public static Collector, ?, Map> mapCollector() { + return Collectors.toMap(Pair::first, Pair::second); + } + + @NotNull + public F first() { + return this.first; + } + + @NotNull + public S second() { + return this.second; + } + + @Override + public String toString() { + return "Pair{" + "first=" + this.first + ", second=" + this.second + '}'; + } +} diff --git a/common/src/main/java/net/infumia/frame/util/PaperLib.java b/common/src/main/java/net/infumia/frame/util/PaperLib.java new file mode 100644 index 0000000..09ae316 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/PaperLib.java @@ -0,0 +1,98 @@ +package net.infumia.frame.util; + +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +public final class PaperLib { + + public static boolean isVersion(final int minor) { + return PaperLib.isVersion(minor, 0); + } + + public static boolean isVersion(final int minor, final int patch) { + return PaperLib.minor() > minor || (PaperLib.minor() >= minor && PaperLib.patch() >= patch); + } + + public static int major() { + return PaperLib.environment.major; + } + + public static int minor() { + return PaperLib.environment.minor; + } + + public static int patch() { + return PaperLib.environment.patch; + } + + public static boolean isPaper() { + return PaperLib.environment.isPaper; + } + + private static final Pattern VERSION_PATTERN = Pattern.compile( + "(?i)\\(MC: (\\d)\\.(\\d+)\\.?(\\d+?)?(?: (Pre-Release|Release Candidate) )?(\\d)?\\)" + ); + + private static final Environment environment = PaperLib.initialize(); + + @NotNull + private static Environment initialize() { + final Matcher matcher = PaperLib.VERSION_PATTERN.matcher(Bukkit.getVersion()); + int major = 1; + int minor = 0; + int patch = 0; + if (matcher.find()) { + final MatchResult matchResult = matcher.toMatchResult(); + try { + major = Integer.parseInt(matchResult.group(1), 10); + } catch (final Exception ignored) {} + try { + minor = Integer.parseInt(matchResult.group(2), 10); + } catch (final Exception ignored) {} + if (matchResult.groupCount() >= 3) { + try { + patch = Integer.parseInt(matchResult.group(3), 10); + } catch (final Exception ignored) {} + } + } + if ( + PaperLib.hasClass("com.destroystokyo.paper.PaperConfig") || + PaperLib.hasClass("io.papermc.paper.configuration.Configuration") + ) { + return new Environment(major, minor, patch, true); + } + return new Environment(major, minor, patch, false); + } + + private static boolean hasClass(@NotNull final String className) { + try { + Class.forName(className); + return true; + } catch (final ClassNotFoundException e) { + return false; + } + } + + static final class Environment { + + private final int major; + private final int minor; + private final int patch; + private final boolean isPaper; + + private Environment( + final int major, + final int minor, + final int patch, + final boolean isPaper + ) { + this.major = major; + this.minor = minor; + this.patch = patch; + this.isPaper = isPaper; + } + } +} diff --git a/common/src/main/java/net/infumia/frame/util/Preconditions.java b/common/src/main/java/net/infumia/frame/util/Preconditions.java new file mode 100644 index 0000000..9da2dc1 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Preconditions.java @@ -0,0 +1,56 @@ +package net.infumia.frame.util; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class Preconditions { + + @Contract("false, _, _ -> fail") + public static void argument( + final boolean check, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + if (!check) { + throw new IllegalArgumentException(String.format(message, args)); + } + } + + @NotNull + @Contract("null, _, _ -> fail") + public static T argumentNotNull( + @Nullable final T object, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + Preconditions.argument(object != null, message, args); + return object; + } + + @Contract("false, _, _ -> fail") + public static void state( + final boolean check, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + if (!check) { + throw new IllegalStateException(String.format(message, args)); + } + } + + @NotNull + @Contract("null, _, _ -> fail") + public static T stateNotNull( + @Nullable final T object, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + Preconditions.state(object != null, message, args); + return object; + } + + private Preconditions() { + throw new IllegalStateException("Utility class!"); + } +} diff --git a/common/src/main/java/net/infumia/frame/util/Reflection.java b/common/src/main/java/net/infumia/frame/util/Reflection.java new file mode 100644 index 0000000..1ba13bd --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Reflection.java @@ -0,0 +1,65 @@ +package net.infumia.frame.util; + +import java.lang.reflect.Field; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class Reflection { + + public static boolean hasClass(@NotNull final String className) { + return Reflection.findClass(className) != null; + } + + @Nullable + public static Class findClass(@NotNull final String className) { + try { + return Class.forName(className); + } catch (final ClassNotFoundException e) { + return null; + } + } + + @NotNull + @SuppressWarnings("unchecked") + public static T findInstanceFromField( + @NotNull final String className, + @NotNull final String fieldName + ) { + final Class cls = Preconditions.argumentNotNull( + Reflection.findClass(className), + "Class '%s' not found", + className + ); + Field field = null; + boolean old = false; + try { + field = cls.getDeclaredField(fieldName); + old = field.isAccessible(); + field.setAccessible(true); + return Preconditions.argumentNotNull( + (T) field.get(null), + "Field '%s' value is null in class '%s'!", + fieldName, + className + ); + } catch (final NoSuchFieldException e) { + throw new RuntimeException( + String.format("Field '%s' not found in class '%s'!", fieldName, className), + e + ); + } catch (final IllegalAccessException e) { + throw new RuntimeException( + String.format("Cannot access to field '%s' in class '%s'!", fieldName, className), + e + ); + } finally { + if (field != null) { + field.setAccessible(old); + } + } + } + + private Reflection() { + throw new IllegalStateException("Utility class!"); + } +} diff --git a/common/src/main/java/net/infumia/frame/util/RunnableThrowable.java b/common/src/main/java/net/infumia/frame/util/RunnableThrowable.java new file mode 100644 index 0000000..a6946ef --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/RunnableThrowable.java @@ -0,0 +1,6 @@ +package net.infumia.frame.util; + +@FunctionalInterface +public interface RunnableThrowable { + void run() throws Throwable; +} diff --git a/common/src/main/java/net/infumia/frame/util/Ticks.java b/common/src/main/java/net/infumia/frame/util/Ticks.java new file mode 100644 index 0000000..09a441e --- /dev/null +++ b/common/src/main/java/net/infumia/frame/util/Ticks.java @@ -0,0 +1,15 @@ +package net.infumia.frame.util; + +import java.time.Duration; +import org.jetbrains.annotations.NotNull; + +public final class Ticks { + + public static int toTicks(@NotNull final Duration duration) { + return (int) (duration.toMillis() / 50L); + } + + private Ticks() { + throw new IllegalStateException("Utility class!"); + } +} diff --git a/common/src/main/java/net/infumia/frame/view/View.java b/common/src/main/java/net/infumia/frame/view/View.java new file mode 100644 index 0000000..66ce068 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/View.java @@ -0,0 +1,14 @@ +package net.infumia.frame.view; + +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.pipeline.Pipelined; +import net.infumia.frame.pipeline.executor.PipelineExecutorView; +import org.jetbrains.annotations.NotNull; + +public interface View extends Pipelined { + @NotNull + ContextInit context(); + + @NotNull + Object instance(); +} diff --git a/common/src/main/java/net/infumia/frame/view/ViewContainer.java b/common/src/main/java/net/infumia/frame/view/ViewContainer.java new file mode 100644 index 0000000..78b48f5 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/ViewContainer.java @@ -0,0 +1,41 @@ +package net.infumia.frame.view; + +import net.infumia.frame.type.InvType; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ViewContainer { + @NotNull + Inventory inventory(); + + @NotNull + InvType type(); + + int size(); + + int slotsCount(); + + int rowsCount(); + + int columnsCount(); + + int firstSlot(); + + int lastSlot(); + + boolean hasItem(int slot); + + void removeItem(int slot); + + void addItem(int slot, @NotNull ItemStack item); + + void open(@NotNull Viewer viewer); + + boolean isPlayerInventory(); + + @Nullable + ViewContainer at(int slot); +} diff --git a/common/src/main/java/net/infumia/frame/view/ViewCreator.java b/common/src/main/java/net/infumia/frame/view/ViewCreator.java new file mode 100644 index 0000000..6591dc2 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/ViewCreator.java @@ -0,0 +1,17 @@ +package net.infumia.frame.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injection.InjectionServicePipeline; +import net.infumia.frame.injector.InjectorRegistry; +import org.jetbrains.annotations.NotNull; + +public interface ViewCreator { + @NotNull + InjectionServicePipeline pipeline(); + + @NotNull + InjectorRegistry injectors(); + + @NotNull + CompletableFuture create(@NotNull Class viewClass); +} diff --git a/common/src/main/java/net/infumia/frame/view/ViewHandler.java b/common/src/main/java/net/infumia/frame/view/ViewHandler.java new file mode 100644 index 0000000..2624828 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/ViewHandler.java @@ -0,0 +1,30 @@ +package net.infumia.frame.view; + +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextResume; +import net.infumia.frame.viewer.ContextualViewer; +import org.jetbrains.annotations.NotNull; + +public interface ViewHandler { + default void onInit(@NotNull final ContextInit ctx) {} + + default void onOpen(@NotNull final ContextOpen ctx) {} + + default void onFirstRender(@NotNull final ContextRender ctx) {} + + default void onViewerAdded(@NotNull final ContextualViewer viewer) {} + + default void onViewerRemoved(@NotNull final ContextualViewer viewer) {} + + default void onResume(@NotNull final ContextResume context) {} + + default void onUpdate(@NotNull final ContextRender ctx) {} + + default void onClick(@NotNull final ContextClick ctx) {} + + default void onClose(@NotNull final ContextClose ctx) {} +} diff --git a/common/src/main/java/net/infumia/frame/view/config/ViewConfig.java b/common/src/main/java/net/infumia/frame/view/config/ViewConfig.java new file mode 100644 index 0000000..ed89fb6 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/ViewConfig.java @@ -0,0 +1,37 @@ +package net.infumia.frame.view.config; + +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import net.infumia.frame.type.InvType; +import net.infumia.frame.view.config.option.ViewConfigOption; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +public interface ViewConfig { + @Nullable + Object title(); + + int size(); + + @NotNull + String@Nullable[] layout(); + + @NotNull + InvType type(); + + @Nullable + Duration updateInterval(); + + @Nullable + Duration interactionDelay(); + + @NotNull + @UnmodifiableView + Map, Object> options(); + + @NotNull + @UnmodifiableView + Collection modifiers(); +} diff --git a/common/src/main/java/net/infumia/frame/view/config/ViewConfigBuilder.java b/common/src/main/java/net/infumia/frame/view/config/ViewConfigBuilder.java new file mode 100644 index 0000000..4a996d9 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/ViewConfigBuilder.java @@ -0,0 +1,54 @@ +package net.infumia.frame.view.config; + +import java.time.Duration; +import net.infumia.frame.type.InvType; +import net.infumia.frame.view.config.option.ViewConfigOption; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ViewConfigBuilder extends ViewConfig { + @NotNull + ViewConfigBuilder title(@NotNull Object title); + + @NotNull + ViewConfigBuilder layout(@NotNull String@Nullable[] layout); + + @NotNull + ViewConfigBuilder size(int size); + + @NotNull + ViewConfigBuilder type(@NotNull InvType type); + + @NotNull + ViewConfigBuilder updateInterval(@NotNull Duration updateInterval); + + @NotNull + ViewConfigBuilder interactionDelay(@NotNull Duration interactionDelay); + + @NotNull + ViewConfigBuilder cancelOnClick(); + + @NotNull + ViewConfigBuilder cancelOnPickup(); + + @NotNull + ViewConfigBuilder cancelOnDrop(); + + @NotNull + ViewConfigBuilder cancelOnDrag(); + + @NotNull + ViewConfigBuilder cancelDefaults(); + + @NotNull + ViewConfigBuilder addOption(@NotNull ViewConfigOption option); + + @NotNull + ViewConfigBuilder addOption(@NotNull ViewConfigOption option, @NotNull T value); + + @NotNull + ViewConfigBuilder addModifier(@NotNull ViewConfigModifier modifier); + + @NotNull + ViewConfigBuilder addModifier(@NotNull ViewConfigModifier... modifiers); +} diff --git a/common/src/main/java/net/infumia/frame/view/config/ViewConfigModifier.java b/common/src/main/java/net/infumia/frame/view/config/ViewConfigModifier.java new file mode 100644 index 0000000..6e4ca3d --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/ViewConfigModifier.java @@ -0,0 +1,7 @@ +package net.infumia.frame.view.config; + +import java.util.function.BiConsumer; +import net.infumia.frame.context.view.ContextOpen; + +@FunctionalInterface +public interface ViewConfigModifier extends BiConsumer {} diff --git a/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOption.java b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOption.java new file mode 100644 index 0000000..3f5a8ba --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOption.java @@ -0,0 +1,23 @@ +package net.infumia.frame.view.config.option; + +import net.infumia.frame.util.Keyed; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ViewConfigOption extends Keyed { + @NotNull + static ViewConfigOption create( + @NotNull final String key, + @Nullable final T defaultValue + ) { + return new ViewConfigOptionImpl<>(key, defaultValue); + } + + @NotNull + static ViewConfigOption create(@NotNull final String key) { + return ViewConfigOption.create(key, null); + } + + @Nullable + T defaultValue(); +} diff --git a/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionController.java b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionController.java new file mode 100644 index 0000000..cbebd39 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionController.java @@ -0,0 +1,12 @@ +package net.infumia.frame.view.config.option; + +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +public interface ViewConfigOptionController { + @NotNull + Optional option(@NotNull ViewConfigOption option); + + @NotNull + Optional optionOrDefault(@NotNull ViewConfigOption option); +} diff --git a/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionImpl.java b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionImpl.java new file mode 100644 index 0000000..3b39415 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptionImpl.java @@ -0,0 +1,56 @@ +package net.infumia.frame.view.config.option; + +import java.util.Objects; +import java.util.StringJoiner; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class ViewConfigOptionImpl implements ViewConfigOption { + + @NotNull + private final String key; + + @Nullable + private final T defaultValue; + + ViewConfigOptionImpl(@NotNull final String key, @Nullable final T defaultValue) { + this.key = key; + this.defaultValue = defaultValue; + } + + @NotNull + @Override + public String key() { + return this.key; + } + + @Nullable + @Override + public T defaultValue() { + return this.defaultValue; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.key); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ViewConfigOption)) { + return false; + } + return Objects.equals(this.key, ((ViewConfigOption) o).key()); + } + + @Override + public String toString() { + return new StringJoiner(", ", ViewConfigOptionImpl.class.getSimpleName() + "[", "]") + .add("key='" + this.key + "'") + .add("defaultValue=" + this.defaultValue) + .toString(); + } +} diff --git a/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptions.java b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptions.java new file mode 100644 index 0000000..5de3458 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/config/option/ViewConfigOptions.java @@ -0,0 +1,18 @@ +package net.infumia.frame.view.config.option; + +import static net.infumia.frame.view.config.option.ViewConfigOption.create; + +public final class ViewConfigOptions { + + public static final ViewConfigOption CANCEL_ON_CLICK = create("cancel-on-click", true); + public static final ViewConfigOption CANCEL_ON_PICKUP = create( + "cancel-on-pickup", + true + ); + public static final ViewConfigOption CANCEL_ON_DROP = create("cancel-on-drop", true); + public static final ViewConfigOption CANCEL_ON_DRAG = create("cancel-on-drag", true); + + private ViewConfigOptions() { + throw new IllegalAccessError("Utility class!"); + } +} diff --git a/common/src/main/java/net/infumia/frame/view/creator/InventoryCreator.java b/common/src/main/java/net/infumia/frame/view/creator/InventoryCreator.java new file mode 100644 index 0000000..90930fd --- /dev/null +++ b/common/src/main/java/net/infumia/frame/view/creator/InventoryCreator.java @@ -0,0 +1,17 @@ +package net.infumia.frame.view.creator; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface InventoryCreator { + @NotNull + Inventory create( + @Nullable InventoryHolder holder, + @NotNull InventoryType type, + int size, + @Nullable Object title + ); +} diff --git a/common/src/main/java/net/infumia/frame/viewer/ContextualViewer.java b/common/src/main/java/net/infumia/frame/viewer/ContextualViewer.java new file mode 100644 index 0000000..0a3227c --- /dev/null +++ b/common/src/main/java/net/infumia/frame/viewer/ContextualViewer.java @@ -0,0 +1,9 @@ +package net.infumia.frame.viewer; + +import net.infumia.frame.context.view.ContextRender; +import org.jetbrains.annotations.NotNull; + +public interface ContextualViewer extends Viewer { + @NotNull + ContextRender context(); +} diff --git a/common/src/main/java/net/infumia/frame/viewer/Viewer.java b/common/src/main/java/net/infumia/frame/viewer/Viewer.java new file mode 100644 index 0000000..6fbfe31 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/viewer/Viewer.java @@ -0,0 +1,22 @@ +package net.infumia.frame.viewer; + +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewContainer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public interface Viewer { + @NotNull + View view(); + + @NotNull + Player player(); + + void close(); + + void open(@NotNull ViewContainer container); + + @NotNull + MetadataAccess metadata(); +} diff --git a/common/src/main/java/net/infumia/frame/viewer/ViewerCreator.java b/common/src/main/java/net/infumia/frame/viewer/ViewerCreator.java new file mode 100644 index 0000000..ac54586 --- /dev/null +++ b/common/src/main/java/net/infumia/frame/viewer/ViewerCreator.java @@ -0,0 +1,10 @@ +package net.infumia.frame.viewer; + +import net.infumia.frame.view.View; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public interface ViewerCreator { + @NotNull + Viewer create(@NotNull Player player, @NotNull View view); +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 0000000..fecd17d --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,12 @@ +import net.infumia.gradle.applyPublish + +applyPublish("core") + +dependencies { + compileOnly(project(":common")) + compileOnly(libs.minecraft.one.eight.eight.paper) + + compileOnly(libs.guice) { isTransitive = false } + compileOnly(libs.geantyref) + compileOnly(libs.adventure.api) +} diff --git a/core/src/main/java/net/infumia/frame/FrameFactoryImpl.java b/core/src/main/java/net/infumia/frame/FrameFactoryImpl.java new file mode 100644 index 0000000..0287275 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/FrameFactoryImpl.java @@ -0,0 +1,41 @@ +package net.infumia.frame; + +import net.infumia.frame.logger.Logger; +import net.infumia.frame.logger.PluginLogger; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public final class FrameFactoryImpl implements FrameFactory { + + public static final FrameFactory INSTANCE = new FrameFactoryImpl(); + + private FrameFactoryImpl() {} + + @NotNull + @Override + public Frame create( + @NotNull final Plugin plugin, + @NotNull final Logger logger, + final boolean unregisterOnDisable + ) { + return new FrameImpl(plugin, logger, unregisterOnDisable); + } + + @NotNull + @Override + public Frame create(@NotNull final Plugin plugin, @NotNull final Logger logger) { + return this.create(plugin, logger, true); + } + + @NotNull + @Override + public Frame create(@NotNull final Plugin plugin, final boolean unregisterOnDisable) { + return this.create(plugin, new PluginLogger(plugin), unregisterOnDisable); + } + + @NotNull + @Override + public Frame create(@NotNull final Plugin plugin) { + return this.create(plugin, true); + } +} diff --git a/core/src/main/java/net/infumia/frame/FrameImpl.java b/core/src/main/java/net/infumia/frame/FrameImpl.java new file mode 100644 index 0000000..08e1688 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/FrameImpl.java @@ -0,0 +1,308 @@ +package net.infumia.frame; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.listener.InventoryListener; +import net.infumia.frame.logger.Logger; +import net.infumia.frame.metadata.MetadataAccessFactory; +import net.infumia.frame.metadata.MetadataAccessFactoryImpl; +import net.infumia.frame.pipeline.executor.PipelineExecutorManager; +import net.infumia.frame.pipeline.executor.PipelineExecutorManagerImpl; +import net.infumia.frame.task.TaskFactory; +import net.infumia.frame.task.TaskFactoryImpl; +import net.infumia.frame.typedkey.TypedKeyStorageFactory; +import net.infumia.frame.typedkey.TypedKeyStorageImmutableBuilder; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewCreator; +import net.infumia.frame.view.ViewCreatorImpl; +import net.infumia.frame.view.ViewEventHandler; +import net.infumia.frame.view.creator.InventoryCreator; +import net.infumia.frame.view.creator.InventoryCreatorBukkit; +import net.infumia.frame.viewer.ViewerCreator; +import net.infumia.frame.viewer.ViewerCreatorImpl; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class FrameImpl implements FrameRich { + + private final Collection> unregisteredViews = ConcurrentHashMap.newKeySet(); + private final Map, View> registeredViews = new ConcurrentHashMap<>(); + private final AtomicBoolean registered = new AtomicBoolean(false); + private final PipelineExecutorManager pipelines = new PipelineExecutorManagerImpl(this); + private final ViewCreator viewCreator = new ViewCreatorImpl(); + private final Logger logger; + private final TaskFactory taskFactory; + private final InventoryListener listener; + private final MetadataAccessFactory metadataAccessFactory; + private final ViewerCreator viewerCreator; + private TypedKeyStorageFactory storageFactory = TypedKeyStorageFactory.create(); + private InventoryCreator inventoryCreator = InventoryCreatorBukkit.INSTANCE; + + FrameImpl( + @NotNull final Plugin plugin, + @NotNull final Logger logger, + final boolean unregisterOnDisable + ) { + this.logger = logger; + this.taskFactory = new TaskFactoryImpl(plugin, logger); + + this.metadataAccessFactory = new MetadataAccessFactoryImpl(plugin); + this.listener = new InventoryListener(plugin, logger, this.metadataAccessFactory, () -> { + if (unregisterOnDisable) { + this.unregister(); + } + }); + this.viewerCreator = new ViewerCreatorImpl(this.metadataAccessFactory); + } + + @NotNull + @Override + public InventoryListener listener() { + return this.listener; + } + + @NotNull + @Override + public Logger logger() { + return this.logger; + } + + @NotNull + @Override + public TaskFactory taskFactory() { + return this.taskFactory; + } + + @NotNull + @Override + public ViewCreator viewCreator() { + return this.viewCreator; + } + + @NotNull + @Override + public ViewerCreator viewerCreator() { + return this.viewerCreator; + } + + @NotNull + @Override + public TypedKeyStorageFactory storageFactory() { + return this.storageFactory; + } + + @Override + public void storageFactory(@NotNull final TypedKeyStorageFactory storageFactory) { + this.storageFactory = storageFactory; + } + + @NotNull + @Override + public InventoryCreator inventoryCreator() { + return this.inventoryCreator; + } + + @Override + public void inventoryCreator(@NotNull final InventoryCreator inventoryCreator) { + this.inventoryCreator = inventoryCreator; + } + + @Override + public void register() { + Preconditions.state( + !this.registered.get(), + "This inventory manager is already registered! #register() method cannot be called twice!" + ); + this.registered.set(true); + this.executeViewCreation(this.unregisteredViews) + .thenCompose(views -> { + this.registeredViews.clear(); + this.registeredViews.putAll( + views + .stream() + .collect( + Collectors.toMap( + view -> view.instance().getClass(), + Function.identity() + ) + ) + ); + return this.pipelines.executeListenersRegistered(); + }) + .exceptionally(throwable -> { + if (throwable instanceof CompletionException) { + throwable = throwable.getCause(); + } + if (throwable != null) { + this.logger.error(throwable, "Error occurred while registering views!"); + this.unregisterInternally(); + } + return null; + }); + } + + @Override + public void unregister() { + this.unregisterInternally(); + } + + @NotNull + @Override + public Frame with(@NotNull final Class viewClass) { + Preconditions.argument(!this.registered.get(), "This inventory manager is registered!"); + this.intoUnregisteredViews(viewClass); + return this; + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> open( + @NotNull final Player player, + @NotNull final Class viewClass + ) { + return this.open(Collections.singleton(player), viewClass); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> open( + @NotNull final Player player, + @NotNull final Class viewClass, + @NotNull final Consumer initialDataConfigurer + ) { + return this.open(Collections.singleton(player), viewClass, initialDataConfigurer); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> open( + @NotNull final Collection players, + @NotNull final Class viewClass + ) { + return this.open(players, viewClass, builder -> {}); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> open( + @NotNull final Collection players, + @NotNull final Class viewClass, + @NotNull final Consumer initialDataConfigurer + ) { + final View view = this.findView(viewClass); + if (!(view instanceof ViewEventHandler)) { + return CompletableFuture.completedFuture(null); + } + final TypedKeyStorageImmutableBuilder builder = + this.storageFactory.createImmutableBuilder(); + initialDataConfigurer.accept(builder); + return CompletableFutureExtensions.logError( + ((ViewEventHandler) view).simulateOpen(players, builder.build()), + this.logger, + "Error occurred while opening view '%s'!", + viewClass + ); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openActive( + @NotNull final Player player, + @NotNull final ContextRender activeContext + ) { + return this.openActive(Collections.singleton(player), activeContext); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openActive( + @NotNull final Collection players, + @NotNull final ContextRender activeContext + ) { + final View view = activeContext.view(); + if (!(view instanceof ViewEventHandler)) { + return CompletableFuture.completedFuture(null); + } + return CompletableFutureExtensions.logError( + ((ViewEventHandler) view).simulateOpenActive(activeContext, players), + this.logger, + "Error occurred while opening an active view '%s'!", + view.instance() + ); + } + + @NotNull + @Override + public PipelineExecutorManager pipelines() { + return this.pipelines; + } + + @NotNull + private View findView(@NotNull final Class viewClass) { + Preconditions.state( + this.registered.get(), + "Before you open a view you must register this inventory manager!" + ); + return Preconditions.argumentNotNull( + this.registeredViews.get(viewClass), + "View '%s' is not registered!", + viewClass + ); + } + + private void intoUnregisteredViews(@NotNull final Class viewClass) { + Preconditions.argument( + !this.unregisteredViews.contains(viewClass), + "View class '%s' already registered.", + viewClass + ); + this.logger.debug("View class '%s' recognized.", viewClass); + this.unregisteredViews.add(viewClass); + } + + @NotNull + private CompletableFuture> executeViewCreation( + @NotNull final Collection> views + ) { + return this.pipelines.executeViewCreated(views).thenCompose( + this.pipelines::executeViewRegistered + ); + } + + private void unregisterInternally() { + this.registered.set(false); + this.metadataAccessFactory.clearCache(Bukkit.getOnlinePlayers()); + HandlerList.unregisterAll(this.listener); + final HashMap, View> views = new HashMap<>(this.registeredViews); + this.registeredViews.clear(); + this.executeViewUnRegistration(views); + } + + private void executeViewUnRegistration(@NotNull final Map, View> views) { + this.pipelines.executeViewUnregistered(views.values()).whenComplete((state, throwable) -> { + if (throwable != null) { + this.logger.error( + throwable, + "Error occurred while unregistering views '%s'!", + views.keySet() + ); + } + }); + } +} diff --git a/core/src/main/java/net/infumia/frame/FrameRich.java b/core/src/main/java/net/infumia/frame/FrameRich.java new file mode 100644 index 0000000..43bcedc --- /dev/null +++ b/core/src/main/java/net/infumia/frame/FrameRich.java @@ -0,0 +1,9 @@ +package net.infumia.frame; + +import net.infumia.frame.listener.InventoryListener; +import org.jetbrains.annotations.NotNull; + +public interface FrameRich extends Frame { + @NotNull + InventoryListener listener(); +} diff --git a/core/src/main/java/net/infumia/frame/InvTypeRich.java b/core/src/main/java/net/infumia/frame/InvTypeRich.java new file mode 100644 index 0000000..aa65522 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/InvTypeRich.java @@ -0,0 +1,144 @@ +package net.infumia.frame; + +import java.util.Arrays; +import net.infumia.frame.type.InvType; +import net.infumia.frame.util.Preconditions; +import org.bukkit.event.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class InvTypeRich { + + private static final int[] EMPTY_RESULT_SLOTS = new int[0]; + + private final InvType type; + private final int maxSize; + private final int rows; + private final int columns; + private final boolean extendable; + private final int@NotNull[] resultSlots; + private final boolean aligned; + + InvTypeRich( + @NotNull final InvType type, + final int maxSize, + final int rows, + final int columns, + final boolean extendable, + final int@NotNull[] resultSlots, + final boolean aligned + ) { + this.type = type; + this.maxSize = maxSize; + this.rows = rows; + this.columns = columns; + this.extendable = extendable; + this.resultSlots = resultSlots; + this.aligned = aligned; + } + + InvTypeRich( + @NotNull final InvType type, + final int maxSize, + final int rows, + final int columns, + final boolean extendable, + final int[] resultSlots + ) { + this(type, maxSize, rows, columns, extendable, resultSlots, true); + } + + InvTypeRich( + @NotNull final InvType type, + final int maxSize, + final int rows, + final int columns, + final boolean extendable + ) { + this(type, maxSize, rows, columns, extendable, InvTypeRich.EMPTY_RESULT_SLOTS); + } + + InvTypeRich(@NotNull final InvType type, final int maxSize, final int rows, final int columns) { + this(type, maxSize, rows, columns, false); + } + + @NotNull + public InvType type() { + return this.type; + } + + public int maxSize() { + return this.maxSize; + } + + public int rows() { + return this.rows; + } + + public int columns() { + return this.columns; + } + + public boolean extendable() { + return this.extendable; + } + + public int@NotNull[] resultSlots() { + return this.resultSlots; + } + + public boolean aligned() { + return this.aligned; + } + + public boolean isResultSlot(final int slot) { + return Arrays.stream(this.resultSlots).anyMatch(resultSlot -> resultSlot == slot); + } + + public boolean canPlayerInteractOn(final int slot) { + return Arrays.stream(this.resultSlots).noneMatch(resultSlot -> resultSlot == slot); + } + + public int normalize(final int size) { + if (size == 0) { + return size; + } + final int fullSize = this.fullSize(size); + Preconditions.argument( + fullSize <= this.maxSize, + "Size cannot exceed container max size of %d (given: %d (%s rows))", + this.maxSize, + fullSize, + size + ); + return fullSize; + } + + @Nullable + public InventoryType toInventoryType() { + switch (this.type) { + case CHEST: + return InventoryType.CHEST; + case PLAYER: + return InventoryType.PLAYER; + default: + return null; + } + } + + private int fullSize(final int size) { + if (size <= this.rows) { + return size * this.columns; + } + if (size == Integer.MAX_VALUE) { + return this.maxSize; + } + Preconditions.argument( + size % this.columns == 0, + "Container size must be a multiple of %d (given: %d)", + this.columns, + size + ); + return size; + } +} diff --git a/core/src/main/java/net/infumia/frame/InvTypes.java b/core/src/main/java/net/infumia/frame/InvTypes.java new file mode 100644 index 0000000..5dd33dc --- /dev/null +++ b/core/src/main/java/net/infumia/frame/InvTypes.java @@ -0,0 +1,112 @@ +package net.infumia.frame; + +import net.infumia.frame.type.InvType; +import org.jetbrains.annotations.NotNull; + +public final class InvTypes { + + public static final InvTypeRich CHEST = new InvTypeRich(InvType.CHEST, 54, 6, 9, true); + public static final InvTypeRich PLAYER = new InvTypeRich(InvType.PLAYER, 36, 3, 9); + public static final InvTypeRich DISPENSER = new InvTypeRich(InvType.DISPENSER, 9, 3, 3); + public static final InvTypeRich DROPPER = new InvTypeRich(InvType.DROPPER, 9, 3, 3); + public static final InvTypeRich FURNACE = new InvTypeRich( + InvType.FURNACE, + 3, + 2, + 2, + false, + new int[] { 2 } + ); + public static final InvTypeRich HOPPER = new InvTypeRich(InvType.HOPPER, 5, 1, 5); + public static final InvTypeRich BLAST_FURNACE = new InvTypeRich( + InvType.BLAST_FURNACE, + 3, + 2, + 2, + false, + new int[] { 2 } + ); + public static final InvTypeRich WORKBENCH = new InvTypeRich( + InvType.WORKBENCH, + 9, + 3, + 3, + false, + new int[] { 3 } + ); + public static final InvTypeRich BREWING_STAND = new InvTypeRich( + InvType.BREWING_STAND, + 4, + 1, + 1, + false, + new int[] { 0, 1, 2 }, + false + ); + public static final InvTypeRich BEACON = new InvTypeRich(InvType.BEACON, 1, 1, 1); + public static final InvTypeRich ANVIL = new InvTypeRich( + InvType.ANVIL, + 3, + 1, + 3, + false, + new int[] { 2 } + ); + public static final InvTypeRich SHULKER_BOX = new InvTypeRich(InvType.SHULKER_BOX, 27, 3, 9); + public static final InvTypeRich SMOKER = new InvTypeRich( + InvType.SMOKER, + 3, + 2, + 2, + false, + new int[] { 2 } + ); + public static final InvTypeRich MERCHANT = new InvTypeRich( + InvType.MERCHANT, + 3, + 1, + 3, + false, + new int[] { 2 } + ); + + @NotNull + public static InvTypeRich fromType(@NotNull final InvType type) { + switch (type) { + case CHEST: + return InvTypes.CHEST; + case PLAYER: + return InvTypes.PLAYER; + case DISPENSER: + return InvTypes.DISPENSER; + case DROPPER: + return InvTypes.DROPPER; + case FURNACE: + return InvTypes.FURNACE; + case HOPPER: + return InvTypes.HOPPER; + case BLAST_FURNACE: + return InvTypes.BLAST_FURNACE; + case WORKBENCH: + return InvTypes.WORKBENCH; + case BREWING_STAND: + return InvTypes.BREWING_STAND; + case BEACON: + return InvTypes.BEACON; + case ANVIL: + return InvTypes.ANVIL; + case SHULKER_BOX: + return InvTypes.SHULKER_BOX; + case SMOKER: + return InvTypes.SMOKER; + case MERCHANT: + return InvTypes.MERCHANT; + default: + throw new IllegalArgumentException("Invalid type: " + type); + } + } + + private InvTypes() { + throw new IllegalAccessError("Utility class!"); + } +} diff --git a/core/src/main/java/net/infumia/frame/SlotConverter.java b/core/src/main/java/net/infumia/frame/SlotConverter.java new file mode 100644 index 0000000..8b57806 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/SlotConverter.java @@ -0,0 +1,31 @@ +package net.infumia.frame; + +import net.infumia.frame.util.Preconditions; + +public final class SlotConverter { + + public static int convertSlot( + final int row, + final int column, + final int maxRowsCount, + final int maxColumnsCount + ) { + Preconditions.argument( + maxRowsCount >= row, + "Row cannot be greater than %d (given %d)", + maxRowsCount, + row + ); + Preconditions.argument( + maxColumnsCount >= column, + "Column cannot be greater than %d (given %d)", + maxColumnsCount, + column + ); + return Math.max(row - 1, 0) * maxColumnsCount + Math.max(column - 1, 0); + } + + private SlotConverter() { + throw new IllegalStateException("Utility class!"); + } +} diff --git a/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderImpl.java b/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderImpl.java new file mode 100644 index 0000000..37627da --- /dev/null +++ b/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderImpl.java @@ -0,0 +1,225 @@ +package net.infumia.frame.config; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import net.infumia.frame.type.InvType; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.util.Reflection; +import net.infumia.frame.view.config.ViewConfigBuilder; +import net.infumia.frame.view.config.ViewConfigModifier; +import net.infumia.frame.view.config.option.ViewConfigOption; +import net.infumia.frame.view.config.option.ViewConfigOptions; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +final class ViewConfigBuilderImpl implements ViewConfigBuilderRich { + + private static final boolean COMPONENT_SUPPORT = Reflection.hasClass( + "net.kyori.adventure.text.Component" + ); + + final Map, Object> options; + final Collection modifiers; + Object title; + int size = -1; + InvType type = InvType.CHEST; + String[] layout; + Duration updateInterval; + Duration interactionDelay; + + ViewConfigBuilderImpl(@NotNull final ViewConfigImpl config) { + this.options = new HashMap<>(config.options); + this.modifiers = new HashSet<>(config.modifiers); + this.title = config.title; + this.size = config.size; + this.type = config.type; + this.layout = config.layout; + this.updateInterval = config.updateInterval; + this.interactionDelay = config.interactionDelay; + } + + ViewConfigBuilderImpl() { + this.options = new HashMap<>(); + this.modifiers = new HashSet<>(); + } + + @Nullable + @Override + public Object title() { + return this.title; + } + + @Override + public int size() { + return this.size; + } + + @NotNull + @Override + public String@Nullable[] layout() { + return this.layout; + } + + @NotNull + @Override + public InvType type() { + return this.type; + } + + @Nullable + @Override + public Duration updateInterval() { + return this.updateInterval; + } + + @Nullable + @Override + public Duration interactionDelay() { + return this.interactionDelay; + } + + @NotNull + @Override + @UnmodifiableView + public Map, Object> options() { + return Collections.unmodifiableMap(this.options); + } + + @NotNull + @Override + @UnmodifiableView + public Collection modifiers() { + return Collections.unmodifiableCollection(this.modifiers); + } + + @NotNull + @Override + public ViewConfigBuilderRich title(@NotNull final Object title) { + ViewConfigBuilderImpl.checkTitleType(title); + this.title = title; + return this; + } + + @NotNull + @Override + public ViewConfigBuilder layout(@NotNull final String@Nullable[] layout) { + this.layout = layout; + return this; + } + + @NotNull + @Override + public ViewConfigBuilder size(final int size) { + this.size = size; + return this; + } + + @NotNull + @Override + public ViewConfigBuilder type(@NotNull final InvType type) { + this.type = type; + return this; + } + + @NotNull + @Override + public ViewConfigBuilder updateInterval(@NotNull final Duration updateInterval) { + this.updateInterval = updateInterval; + return this; + } + + @NotNull + @Override + public ViewConfigBuilder interactionDelay(@NotNull final Duration interactionDelay) { + this.interactionDelay = interactionDelay; + return this; + } + + @NotNull + @Override + public ViewConfigBuilderRich cancelOnClick() { + return this.addOption(ViewConfigOptions.CANCEL_ON_CLICK); + } + + @NotNull + @Override + public ViewConfigBuilderRich cancelOnPickup() { + return this.addOption(ViewConfigOptions.CANCEL_ON_PICKUP); + } + + @NotNull + @Override + public ViewConfigBuilderRich cancelOnDrop() { + return this.addOption(ViewConfigOptions.CANCEL_ON_DROP); + } + + @NotNull + @Override + public ViewConfigBuilderRich cancelOnDrag() { + return this.addOption(ViewConfigOptions.CANCEL_ON_DRAG); + } + + @NotNull + @Override + public ViewConfigBuilderRich cancelDefaults() { + this.cancelOnClick().cancelOnPickup().cancelOnDrop().cancelOnDrag(); + return this; + } + + @NotNull + @Override + public ViewConfigBuilderRich addOption(@NotNull final ViewConfigOption option) { + return this.addOption( + option, + Preconditions.argumentNotNull( + option.defaultValue(), + "Option '%s' does not have a default value! Please use #addOption(ViewConfigOption, T) method instead!" + ) + ); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public ViewConfigBuilderRich addOption( + @NotNull final ViewConfigOption option, + @NotNull final T value + ) { + this.options.put((ViewConfigOption) option, value); + return this; + } + + @NotNull + @Override + public ViewConfigBuilderRich addModifier(@NotNull final ViewConfigModifier modifier) { + this.modifiers.add(modifier); + return this; + } + + @NotNull + @Override + public ViewConfigBuilderRich addModifier(@NotNull final ViewConfigModifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + @NotNull + @Override + public ViewConfigRich build() { + return new ViewConfigImpl(this); + } + + private static void checkTitleType(@NotNull final Object title) { + boolean check = title instanceof String; + if (ViewConfigBuilderImpl.COMPONENT_SUPPORT) { + check |= title instanceof Component; + } + Preconditions.argument(check, "Title must be only String or Component!"); + } +} diff --git a/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderRich.java b/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderRich.java new file mode 100644 index 0000000..945c343 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/config/ViewConfigBuilderRich.java @@ -0,0 +1,14 @@ +package net.infumia.frame.config; + +import net.infumia.frame.view.config.ViewConfigBuilder; +import org.jetbrains.annotations.NotNull; + +public interface ViewConfigBuilderRich extends ViewConfigBuilder { + @NotNull + static ViewConfigBuilderRich create() { + return new ViewConfigBuilderImpl(); + } + + @NotNull + ViewConfigRich build(); +} diff --git a/core/src/main/java/net/infumia/frame/config/ViewConfigImpl.java b/core/src/main/java/net/infumia/frame/config/ViewConfigImpl.java new file mode 100644 index 0000000..d666128 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/config/ViewConfigImpl.java @@ -0,0 +1,111 @@ +package net.infumia.frame.config; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import net.infumia.frame.type.InvType; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.config.ViewConfigModifier; +import net.infumia.frame.view.config.option.ViewConfigOption; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +final class ViewConfigImpl implements ViewConfigRich { + + final Map, Object> options; + final Collection modifiers; + final Object title; + final int size; + final InvType type; + final String[] layout; + final Duration updateInterval; + final Duration interactionDelay; + + ViewConfigImpl(@NotNull final ViewConfigBuilderImpl builder) { + Preconditions.argumentNotNull(builder.title, "Title cannot be null!"); + this.options = Collections.unmodifiableMap(builder.options); + this.modifiers = Collections.unmodifiableCollection(builder.modifiers); + this.title = builder.title; + this.size = builder.size; + this.type = builder.type; + this.layout = builder.layout; + this.updateInterval = builder.updateInterval; + this.interactionDelay = builder.interactionDelay; + } + + @NotNull + @Override + public Object title() { + return this.size; + } + + @NotNull + @Override + public ViewConfigBuilderRich toBuilder() { + return new ViewConfigBuilderImpl(this); + } + + @Override + public int size() { + return this.size; + } + + @NotNull + @Override + public String@Nullable[] layout() { + return this.layout; + } + + @NotNull + @Override + public InvType type() { + return this.type; + } + + @Nullable + @Override + public Duration updateInterval() { + return this.updateInterval; + } + + @Nullable + @Override + public Duration interactionDelay() { + return this.interactionDelay; + } + + @NotNull + @Override + @UnmodifiableView + public Map, Object> options() { + return this.options; + } + + @NotNull + @Override + @UnmodifiableView + public Collection modifiers() { + return this.modifiers; + } + + @Override + @NotNull + @SuppressWarnings("unchecked") + public Optional option(@NotNull final ViewConfigOption option) { + return Optional.ofNullable(this.options.get(option)).map(value -> (T) value); + } + + @NotNull + @Override + public Optional optionOrDefault(@NotNull final ViewConfigOption option) { + final Optional value = this.option(option); + if (value.isPresent()) { + return value; + } else { + return Optional.ofNullable(option.defaultValue()); + } + } +} diff --git a/core/src/main/java/net/infumia/frame/config/ViewConfigRich.java b/core/src/main/java/net/infumia/frame/config/ViewConfigRich.java new file mode 100644 index 0000000..6b61c40 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/config/ViewConfigRich.java @@ -0,0 +1,14 @@ +package net.infumia.frame.config; + +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.view.config.option.ViewConfigOptionController; +import org.jetbrains.annotations.NotNull; + +public interface ViewConfigRich extends ViewConfig, ViewConfigOptionController { + @NotNull + @Override + Object title(); + + @NotNull + ViewConfigBuilderRich toBuilder(); +} diff --git a/core/src/main/java/net/infumia/frame/context/ContextBaseImpl.java b/core/src/main/java/net/infumia/frame/context/ContextBaseImpl.java new file mode 100644 index 0000000..667f796 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/ContextBaseImpl.java @@ -0,0 +1,199 @@ +package net.infumia.frame.context; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.value.StateValueHost; +import net.infumia.frame.state.value.StateValueHostImpl; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.typedkey.TypedKeyStorageImmutableBuilder; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.View; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ContextBaseImpl extends ContextImpl implements ContextBaseRich { + + private final UUID id; + private final StateValueHost stateValueHost; + private final View view; + private final ViewConfig initialConfig; + private final Collection viewers; + private final TypedKeyStorageImmutable initialData; + private final Viewer singleViewer; + + public ContextBaseImpl( + @NotNull final Context context, + @NotNull final UUID id, + @NotNull final View view, + @NotNull final ViewConfig initialConfig, + @NotNull final Collection viewers, + @NotNull final TypedKeyStorageImmutable initialData, + @Nullable final Viewer singleViewer + ) { + super(context); + this.id = id; + this.view = view; + this.initialConfig = initialConfig; + this.viewers = new HashSet<>(viewers); + this.initialData = initialData; + this.singleViewer = singleViewer; + this.stateValueHost = new StateValueHostImpl(this); + } + + public ContextBaseImpl(@NotNull final ContextBase context) { + super(context); + this.id = context.id(); + this.view = context.view(); + this.initialConfig = context.initialConfig(); + this.viewers = new HashSet<>(context.viewers()); + this.initialData = context.initialData(); + this.singleViewer = context.viewer(); + this.stateValueHost = context.stateValueHost(); + } + + @NotNull + @Override + public StateValueHost stateValueHost() { + return this.stateValueHost; + } + + @NotNull + @Override + public UUID id() { + return this.id; + } + + @NotNull + @Override + public View view() { + return this.view; + } + + @NotNull + @Override + public ViewConfig initialConfig() { + return this.initialConfig; + } + + @NotNull + @Override + public TypedKeyStorageImmutable initialData() { + return this.initialData; + } + + @NotNull + @Override + public Collection viewers() { + synchronized (this.viewers) { + return Collections.unmodifiableCollection(this.viewers); + } + } + + @NotNull + @Override + public Viewer viewer() { + return this.viewerOrThrow("viewer"); + } + + @Override + public boolean sharedView() { + return this.singleViewer == null || this.viewers().size() > 1; + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openForEveryone( + @NotNull final Class viewClass + ) { + return this.manager() + .open( + this.viewers().stream().map(Viewer::player).collect(Collectors.toSet()), + viewClass + ); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openForEveryone( + @NotNull final Class viewClass, + @NotNull final Consumer initialDataConfigurer + ) { + return this.manager() + .open( + this.viewers().stream().map(Viewer::player).collect(Collectors.toSet()), + viewClass, + initialDataConfigurer + ); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openForViewer( + @NotNull final Class viewClass + ) { + return this.manager().open(this.viewerOrThrow("openForViewer").player(), viewClass); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> openForViewer( + @NotNull final Class viewClass, + @NotNull final Consumer initialData + ) { + return this.manager() + .open(this.viewerOrThrow("openForViewer").player(), viewClass, initialData); + } + + @Override + public void addViewer(@NotNull final Viewer viewer) { + synchronized (this.viewers) { + this.viewers.add(viewer); + } + } + + @Override + public void removeViewer(@NotNull final Viewer viewer) { + synchronized (this.viewers) { + this.viewers.remove(viewer); + } + } + + @NotNull + @Override + public Viewer viewerOrThrow(@NotNull final String methodName) { + Preconditions.state( + !this.sharedView(), + "You cannot use #%s() method if it's a shared view!", + methodName + ); + return Preconditions.argumentNotNull( + this.singleViewer, + "Viewer not set even tough it's not a shared view!" + ); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.id); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ContextBase)) { + return false; + } + return Objects.equals(this.id, ((ContextBase) o).id()); + } +} diff --git a/core/src/main/java/net/infumia/frame/context/ContextBaseRich.java b/core/src/main/java/net/infumia/frame/context/ContextBaseRich.java new file mode 100644 index 0000000..4e451df --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/ContextBaseRich.java @@ -0,0 +1,13 @@ +package net.infumia.frame.context; + +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface ContextBaseRich extends ContextRich, ContextBase { + void addViewer(@NotNull Viewer viewer); + + void removeViewer(@NotNull Viewer viewer); + + @NotNull + Viewer viewerOrThrow(@NotNull String methodName); +} diff --git a/core/src/main/java/net/infumia/frame/context/ContextImpl.java b/core/src/main/java/net/infumia/frame/context/ContextImpl.java new file mode 100644 index 0000000..e12cbaf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/ContextImpl.java @@ -0,0 +1,55 @@ +package net.infumia.frame.context; + +import net.infumia.frame.Frame; +import net.infumia.frame.state.StateFactory; +import net.infumia.frame.state.StateFactoryImpl; +import net.infumia.frame.state.StateRegistry; +import net.infumia.frame.typedkey.TypedKeyStorage; +import org.jetbrains.annotations.NotNull; + +public class ContextImpl implements ContextRich { + + private final Frame manager; + private final TypedKeyStorage instances; + private final StateRegistry stateRegistry; + private final StateFactory stateFactory; + + public ContextImpl( + @NotNull final Frame manager, + @NotNull final TypedKeyStorage instances, + @NotNull final StateRegistry stateRegistry + ) { + this.manager = manager; + this.instances = instances; + this.stateRegistry = stateRegistry; + this.stateFactory = new StateFactoryImpl(stateRegistry); + } + + public ContextImpl(@NotNull final Context context) { + this(context.manager(), context.instances(), ((ContextRich) context).stateRegistry()); + } + + @NotNull + @Override + public Frame manager() { + return this.manager; + } + + @NotNull + @Override + public TypedKeyStorage instances() { + return this.instances; + } + + @NotNull + @Override + public StateFactory stateFactory() { + return this.stateFactory; + } + + @NotNull + @Override + public StateRegistry stateRegistry() { + return this.stateRegistry; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/ContextRich.java b/core/src/main/java/net/infumia/frame/context/ContextRich.java new file mode 100644 index 0000000..2fc3f8c --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/ContextRich.java @@ -0,0 +1,9 @@ +package net.infumia.frame.context; + +import net.infumia.frame.state.StateRegistry; +import org.jetbrains.annotations.NotNull; + +public interface ContextRich extends Context { + @NotNull + StateRegistry stateRegistry(); +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementClearImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementClearImpl.java new file mode 100644 index 0000000..71d1312 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementClearImpl.java @@ -0,0 +1,27 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderImpl; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public final class ContextElementClearImpl + extends ContextRenderImpl + implements ContextElementClear { + + private final Element element; + + public ContextElementClearImpl( + @NotNull final ContextRender context, + @NotNull final Element element + ) { + super(context); + this.element = element; + } + + @NotNull + @Override + public Element element() { + return this.element; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementClickImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementClickImpl.java new file mode 100644 index 0000000..b97c124 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementClickImpl.java @@ -0,0 +1,29 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextClickImpl; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public class ContextElementClickImpl extends ContextClickImpl implements ContextElementClick { + + private final Element element; + + public ContextElementClickImpl( + @NotNull final ContextClick context, + @NotNull final Element element + ) { + super(context); + this.element = element; + } + + public ContextElementClickImpl(@NotNull final ContextElementClick context) { + this(context, context.element()); + } + + @NotNull + @Override + public Element element() { + return this.element; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementItemClickImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemClickImpl.java new file mode 100644 index 0000000..5f48271 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemClickImpl.java @@ -0,0 +1,25 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.jetbrains.annotations.NotNull; + +public final class ContextElementItemClickImpl + extends ContextElementClickImpl + implements ContextElementItemClick { + + private final ElementItem element; + + public ContextElementItemClickImpl( + @NotNull final ContextElementClick context, + @NotNull final ElementItem element + ) { + super(context); + this.element = element; + } + + @NotNull + @Override + public ElementItem element() { + return this.element; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementItemRenderImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemRenderImpl.java new file mode 100644 index 0000000..076c0f5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemRenderImpl.java @@ -0,0 +1,51 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class ContextElementItemRenderImpl + extends ContextElementRenderImpl + implements ContextElementItemRender { + + private final ElementItem element; + private int modifiedSlot; + private ItemStack modifiedItem; + + public ContextElementItemRenderImpl( + @NotNull final ContextElementRender context, + @NotNull final ElementItem element + ) { + super(context); + this.element = element; + this.modifiedSlot = element.slot(); + this.modifiedItem = element.item(); + } + + @NotNull + @Override + public ElementItem element() { + return this.element; + } + + @Override + public int modifiedSlot() { + return this.modifiedSlot; + } + + @Override + public void modifySlot(final int slot) { + this.modifiedSlot = slot; + } + + @NotNull + @Override + public ItemStack modifiedItem() { + return this.modifiedItem; + } + + @Override + public void modifyItem(@NotNull final ItemStack newItem) { + this.modifiedItem = newItem; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdateImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdateImpl.java new file mode 100644 index 0000000..bdd0059 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementItemUpdateImpl.java @@ -0,0 +1,25 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.element.ElementItem; +import org.jetbrains.annotations.NotNull; + +public final class ContextElementItemUpdateImpl + extends ContextElementUpdateImpl + implements ContextElementItemUpdate { + + private final ElementItem element; + + public ContextElementItemUpdateImpl( + @NotNull final ContextElementUpdate context, + @NotNull final ElementItem element1 + ) { + super(context); + this.element = element1; + } + + @NotNull + @Override + public ElementItem element() { + return this.element; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementRenderImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementRenderImpl.java new file mode 100644 index 0000000..a2892f1 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementRenderImpl.java @@ -0,0 +1,29 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderImpl; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public class ContextElementRenderImpl extends ContextRenderImpl implements ContextElementRender { + + private final Element element; + + public ContextElementRenderImpl( + @NotNull final ContextRender context, + @NotNull final Element element + ) { + super(context); + this.element = element; + } + + @NotNull + @Override + public Element element() { + return this.element; + } + + public ContextElementRenderImpl(@NotNull final ContextElementRender context) { + this(context, context.element()); + } +} diff --git a/core/src/main/java/net/infumia/frame/context/element/ContextElementUpdateImpl.java b/core/src/main/java/net/infumia/frame/context/element/ContextElementUpdateImpl.java new file mode 100644 index 0000000..c7dbd7c --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/element/ContextElementUpdateImpl.java @@ -0,0 +1,49 @@ +package net.infumia.frame.context.element; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderImpl; +import net.infumia.frame.element.Element; +import org.jetbrains.annotations.NotNull; + +public class ContextElementUpdateImpl extends ContextRenderImpl implements ContextElementUpdate { + + private final Element element; + private final boolean forced; + private boolean cancelled; + + public ContextElementUpdateImpl( + @NotNull final ContextRender context, + @NotNull final Element element, + final boolean forced + ) { + super(context); + this.element = element; + this.forced = forced; + } + + public ContextElementUpdateImpl(@NotNull final ContextElementUpdate context) { + this(context, context.element(), context.forced()); + this.cancelled = context.cancelled(); + } + + @NotNull + @Override + public Element element() { + return this.element; + } + + @Override + public boolean forced() { + return this.forced; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextClickImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextClickImpl.java new file mode 100644 index 0000000..2b898ad --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextClickImpl.java @@ -0,0 +1,129 @@ +package net.infumia.frame.context.view; + +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.viewer.ContextualViewer; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ContextClickImpl extends ContextRenderImpl implements ContextClick { + + private final ContextualViewer clicker; + private final InventoryClickEvent event; + + public ContextClickImpl( + @NotNull final ContextualViewer clicker, + @NotNull final InventoryClickEvent event + ) { + super(clicker.context()); + this.clicker = clicker; + this.event = event; + } + + public ContextClickImpl(@NotNull final ContextClick context) { + this(context.clicker(), context.event()); + } + + @NotNull + @Override + public InventoryClickEvent event() { + return this.event; + } + + @NotNull + @Override + public ContextualViewer clicker() { + return this.clicker; + } + + @NotNull + @Override + public ClickType clickType() { + return this.event.getClick(); + } + + @Nullable + @Override + public ViewContainer clickedContainer() { + return this.container().at(this.clickedSlot()); + } + + @Override + public int clickedSlot() { + return this.event.getSlot(); + } + + @Override + public int clickedSlotRaw() { + return this.event.getRawSlot(); + } + + @NotNull + @Override + public InventoryType.SlotType clickedSlotType() { + return this.event.getSlotType(); + } + + @Override + public boolean leftClick() { + return this.event.isLeftClick(); + } + + @Override + public boolean rightClick() { + return this.event.isRightClick(); + } + + @Override + public boolean middleClick() { + return this.clickType() == ClickType.MIDDLE; + } + + @Override + public boolean shiftClick() { + return this.event.isShiftClick(); + } + + @Override + public boolean keyboardClick() { + return this.clickType().isKeyboardClick(); + } + + @Override + public boolean outsideClicked() { + return this.clickedSlotType() == InventoryType.SlotType.OUTSIDE; + } + + @Override + public boolean entityContainer() { + return this.event.getClickedInventory() instanceof PlayerInventory; + } + + @Override + public boolean layoutSlot() { + return this.layouts().values().stream().anyMatch(slot -> slot.contains(this.clickedSlot())); + } + + @Override + public boolean layoutSlot(final char character) { + final LayoutSlot slot = this.layouts().get(character); + if (slot == null) { + return false; + } + return slot.contains(this.clickedSlot()); + } + + @Override + public boolean cancelled() { + return this.event.isCancelled(); + } + + @Override + public void cancelled(final boolean cancelled) { + this.event.setCancelled(cancelled); + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextCloseImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextCloseImpl.java new file mode 100644 index 0000000..53f0621 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextCloseImpl.java @@ -0,0 +1,38 @@ +package net.infumia.frame.context.view; + +import net.infumia.frame.viewer.ContextualViewer; +import org.jetbrains.annotations.NotNull; + +public final class ContextCloseImpl extends ContextRenderImpl implements ContextClose { + + private final ContextualViewer viewer; + private final boolean forced; + private boolean cancelled; + + public ContextCloseImpl(@NotNull final ContextualViewer viewer, final boolean forced) { + super(viewer.context()); + this.viewer = viewer; + this.forced = forced; + } + + @NotNull + @Override + public ContextualViewer viewer() { + return this.viewer; + } + + @Override + public boolean forced() { + return this.forced; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextInitImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextInitImpl.java new file mode 100644 index 0000000..d67cf45 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextInitImpl.java @@ -0,0 +1,39 @@ +package net.infumia.frame.context.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.Context; +import net.infumia.frame.context.ContextImpl; +import net.infumia.frame.view.config.ViewConfigBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ContextInitImpl extends ContextImpl implements ContextInit { + + private final ViewConfigBuilder configBuilder; + private CompletableFuture waitUntil; + + public ContextInitImpl( + @NotNull final Context context, + @NotNull final ViewConfigBuilder configBuilder + ) { + super(context); + this.configBuilder = configBuilder; + } + + @NotNull + @Override + public ViewConfigBuilder configBuilder() { + return this.configBuilder; + } + + @Override + public void waitUntil(@Nullable final CompletableFuture waitUntil) { + this.waitUntil = waitUntil; + } + + @Nullable + @Override + public CompletableFuture waitUntil() { + return this.waitUntil; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextOpenImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextOpenImpl.java new file mode 100644 index 0000000..31fc863 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextOpenImpl.java @@ -0,0 +1,61 @@ +package net.infumia.frame.context.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.config.ViewConfigBuilderRich; +import net.infumia.frame.config.ViewConfigRich; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.ContextBaseImpl; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.view.config.ViewConfigBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ContextOpenImpl extends ContextBaseImpl implements ContextOpen { + + private ViewConfigBuilder modifiedConfig; + private CompletableFuture waitUntil; + private boolean cancelled; + + public ContextOpenImpl(@NotNull final ContextBase context) { + super(context); + } + + @Override + public void waitUntil(@Nullable final CompletableFuture waitUntil) { + this.waitUntil = waitUntil; + } + + @Nullable + @Override + public CompletableFuture waitUntil() { + return this.waitUntil; + } + + @NotNull + @Override + public ViewConfigBuilder modifyConfig() { + if (this.modifiedConfig == null) { + this.modifiedConfig = ((ViewConfigRich) this.initialConfig()).toBuilder(); + } + return this.modifiedConfig; + } + + @NotNull + @Override + public ViewConfig buildFinalConfig() { + if (this.modifiedConfig == null) { + return this.initialConfig(); + } + return ((ViewConfigBuilderRich) this.modifiedConfig).build(); + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextRenderImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextRenderImpl.java new file mode 100644 index 0000000..0cbd4d4 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextRenderImpl.java @@ -0,0 +1,333 @@ +package net.infumia.frame.context.view; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.ContextBaseImpl; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.element.ElementItemBuilderImpl; +import net.infumia.frame.element.ElementItemBuilderRich; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.executor.PipelineExecutorRender; +import net.infumia.frame.pipeline.executor.PipelineExecutorRenderImpl; +import net.infumia.frame.pipeline.executor.PipelineExecutorViewer; +import net.infumia.frame.pipeline.executor.PipelineExecutorViewerImpl; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.slot.SlotFinder; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ContextRenderImpl extends ContextBaseImpl implements ContextRenderRich { + + private final PipelineExecutorRender pipelines; + private final PipelineExecutorViewer pipelinesViewer; + private final SlotFinder slotFinder; + private final List elements; + private final ViewContainer container; + private final ViewConfig config; + private final Map layouts; + private Closeable updateTask; + + public ContextRenderImpl( + @NotNull final ContextBase context, + @NotNull final ViewContainer container, + @NotNull final ViewConfig config, + @NotNull final Map layouts + ) { + super(context); + this.container = container; + this.config = config; + this.layouts = layouts; + this.slotFinder = new SlotFinder(this); + this.elements = new ArrayList<>(); + this.pipelines = new PipelineExecutorRenderImpl(this); + this.pipelinesViewer = new PipelineExecutorViewerImpl(this); + } + + public ContextRenderImpl(@NotNull final ContextRender context) { + super(context); + this.container = context.container(); + this.config = context.config(); + this.layouts = context.layouts(); + this.slotFinder = ((ContextRenderRich) context).slotFinder(); + this.elements = new ArrayList<>(context.elements()); + this.pipelines = context.pipelines(); + this.pipelinesViewer = context.pipelinesViewer(); + this.updateTask = ((ContextRenderRich) context).updateTask(); + } + + @NotNull + @Override + public ViewContainer container() { + return this.container; + } + + @NotNull + @Override + public ViewConfig config() { + return this.config; + } + + @NotNull + @Override + public Map layouts() { + return this.layouts; + } + + @Override + public void back() { + final Viewer viewer = this.viewerOrThrow("back"); + final MetadataAccess metadata = viewer.metadata(); + final Deque previousContexts = metadata.get( + MetadataKeyHolder.PREVIOUS_VIEWS + ); + if (previousContexts == null) { + return; + } + final ContextRender previousContext = previousContexts.pollLast(); + if (previousContext == null) { + metadata.remove(MetadataKeyHolder.PREVIOUS_VIEWS); + return; + } else if (previousContexts.isEmpty()) { + metadata.remove(MetadataKeyHolder.PREVIOUS_VIEWS); + } + CompletableFutureExtensions.logError( + this.manager() + .openActive(viewer.player(), previousContext) + .thenCompose(__ -> + ((ContextRenderRich) previousContext).simulateResume( + this, + Collections.singleton(viewer) + ) + ), + this.manager().logger(), + "An error occurred while going back to view '%s'.", + previousContext.view().instance() + ); + } + + @Override + public boolean canBack() { + final Deque previousViews = + this.viewerOrThrow("canBack").metadata().get(MetadataKeyHolder.PREVIOUS_VIEWS); + return previousViews != null && !previousViews.isEmpty(); + } + + @Override + public void closeForEveryone() { + this.closeForEveryone(true); + } + + @Override + public void closeForViewer() { + this.closeForViewer(true); + } + + @Override + public void closeForEveryone(final boolean forced) { + for (final Viewer viewer : this.viewers()) { + viewer.metadata().setFixed(MetadataKeyHolder.FORCED_CLOSE, true); + viewer.close(); + } + } + + @Override + public void closeForViewer(final boolean forced) { + Preconditions.state( + !this.sharedView(), + "You cannot use #closeForViewer() method if it's a shared view!" + ); + final Viewer viewer = this.viewer(); + if (forced) { + viewer.metadata().setFixed(MetadataKeyHolder.FORCED_CLOSE, true); + } + viewer.close(); + } + + @NotNull + @Override + public PipelineExecutorViewer pipelinesViewer() { + return this.pipelinesViewer; + } + + @NotNull + @Override + public SlotFinder slotFinder() { + return this.slotFinder; + } + + @Override + public void updateTask(@Nullable final Closeable task) { + this.updateTask = task; + } + + @Nullable + @Override + public Closeable updateTask() { + return this.updateTask; + } + + @Override + public void addElement(@NotNull final Element element) { + this.elements.add(element); + } + + @NotNull + @Override + public CompletableFuture simulateFirstRender() { + return this.pipelines.executeFirstRender() + .thenCompose(__ -> this.simulateNavigate(this.viewers())); + } + + @NotNull + @Override + public CompletableFuture simulateNavigate( + @NotNull final Collection viewers + ) { + return this.pipelines.executeTransition(viewers) + .thenCompose(__ -> this.pipelinesViewer.executeAdded(viewers)) + .thenCompose(__ -> this.pipelines.executeOpenContainer(viewers)) + .thenCompose(__ -> this.pipelines.executeStartUpdate()); + } + + @NotNull + @Override + public CompletableFuture simulateResume( + @NotNull final ContextRender from, + @NotNull final Collection viewers + ) { + return this.pipelines.executeResume(from, viewers); + } + + @NotNull + @Override + public PipelineExecutorRender pipelines() { + return this.pipelines; + } + + @NotNull + @Override + public List elements() { + return Collections.unmodifiableList(this.elements); + } + + @NotNull + @Override + public ElementItemBuilder unsetSlot() { + return this.newRegisteredBuilder(); + } + + @NotNull + @Override + public ElementItemBuilder layout(final char layout) { + final LayoutSlot layoutSlot = Preconditions.argumentNotNull( + this.slotFinder.findLayoutSlot(layout), + "Missing layout character '%s'", + layout + ); + final ElementItemBuilder builder = this.newUnregisteredBuilder(); + layoutSlot.builderFactory(__ -> builder); + return builder; + } + + @NotNull + @Override + public ElementItemBuilder layout(final char layout, @NotNull final ItemStack item) { + return this.layout(layout).item(item); + } + + @Override + public void layout( + final char layout, + @NotNull final BiConsumer configurer + ) { + final LayoutSlot layoutSlot = Preconditions.argumentNotNull( + this.slotFinder.findLayoutSlot(layout), + "Missing layout character '%s'", + layout + ); + layoutSlot.builderFactory(index -> { + final ElementItemBuilder builder = this.newUnregisteredBuilder(); + configurer.accept(index, builder); + return builder; + }); + } + + @NotNull + @Override + public ElementItemBuilder slot(final int slot) { + return this.unsetSlot().slot(slot); + } + + @NotNull + @Override + public ElementItemBuilder position(final int row, final int column) { + return this.slot(this.slotFinder.toSlot(row, column)); + } + + @NotNull + @Override + public ElementItemBuilder firstSlot() { + return this.slot(this.slotFinder.findFirstSlot()); + } + + @NotNull + @Override + public ElementItemBuilder lastSlot() { + return this.slot(this.slotFinder.findLastSlot()); + } + + @Override + public void availableSlot(@NotNull final ItemStack item) { + this.availableSlot((__, builder) -> builder.item(item)); + } + + @Override + public void availableSlot(@NotNull final BiConsumer configurer) { + this.slotFinder.addAvailableSlotFinder((index, slot) -> { + final ElementItemBuilder builder = this.newUnregisteredBuilder().slot(slot); + configurer.accept(index, builder); + return builder; + }); + } + + @NotNull + @Override + public ElementItemBuilder resultSlot() { + return this.slot(this.slotFinder.findResultSlot()); + } + + @NotNull + @Override + public ElementItemBuilder resultSlot(@NotNull final ItemStack item) { + return this.resultSlot().item(item); + } + + @NotNull + private ElementItemBuilderRich newUnregisteredBuilder() { + return new ElementItemBuilderImpl(); + } + + @NotNull + private ElementItemBuilderRich newRegisteredBuilder() { + final ElementItemBuilderRich builder = this.newUnregisteredBuilder(); + this.slotFinder.addNonRenderedBuilder(builder); + return builder; + } +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextRenderRich.java b/core/src/main/java/net/infumia/frame/context/view/ContextRenderRich.java new file mode 100644 index 0000000..bcaeede --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextRenderRich.java @@ -0,0 +1,36 @@ +package net.infumia.frame.context.view; + +import java.io.Closeable; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBaseRich; +import net.infumia.frame.element.Element; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.slot.SlotFinder; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ContextRenderRich extends ContextBaseRich, ContextRender { + @NotNull + SlotFinder slotFinder(); + + void updateTask(@Nullable Closeable task); + + @Nullable + Closeable updateTask(); + + void addElement(@NotNull Element element); + + @NotNull + CompletableFuture simulateFirstRender(); + + @NotNull + CompletableFuture simulateNavigate(@NotNull Collection viewers); + + @NotNull + CompletableFuture simulateResume( + @NotNull ContextRender from, + @NotNull Collection viewers + ); +} diff --git a/core/src/main/java/net/infumia/frame/context/view/ContextResumeImpl.java b/core/src/main/java/net/infumia/frame/context/view/ContextResumeImpl.java new file mode 100644 index 0000000..2a92c7c --- /dev/null +++ b/core/src/main/java/net/infumia/frame/context/view/ContextResumeImpl.java @@ -0,0 +1,33 @@ +package net.infumia.frame.context.view; + +import java.util.Collection; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ContextResumeImpl extends ContextRenderImpl implements ContextResume { + + private final ContextRender from; + private final Collection resumedViewers; + + public ContextResumeImpl( + @NotNull final ContextRender context, + @NotNull final ContextRender from, + @NotNull final Collection resumedViewers + ) { + super(context); + this.from = from; + this.resumedViewers = resumedViewers; + } + + @NotNull + @Override + public ContextRender from() { + return this.from; + } + + @NotNull + @Override + public Collection resumedViewers() { + return this.resumedViewers; + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementBuilderImpl.java b/core/src/main/java/net/infumia/frame/element/ElementBuilderImpl.java new file mode 100644 index 0000000..e1a7372 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementBuilderImpl.java @@ -0,0 +1,139 @@ +package net.infumia.frame.element; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; + +public class ElementBuilderImpl implements ElementBuilderRich { + + Element root; + boolean cancelOnClick; + boolean closeOnClick; + boolean updateOnClick; + Predicate displayIf; + Collection> updateOnStateChange; + Collection> updateOnStateAccess; + + public ElementBuilderImpl(@NotNull final Element element) { + this.root = ((ElementRich) element).root(); + this.cancelOnClick = element.cancelOnClick(); + this.closeOnClick = element.closeOnClick(); + this.updateOnClick = element.updateOnClick(); + this.displayIf = element.displayIf(); + this.updateOnStateChange = element.updateOnStateChange(); + this.updateOnStateAccess = element.updateOnStateAccess(); + } + + public ElementBuilderImpl() {} + + @NotNull + @Override + public ElementBuilder root(@NotNull final Element root) { + this.root = root; + return this; + } + + @NotNull + @Override + public Element build(@NotNull final ContextBase context) { + return new ElementImpl(this, context); + } + + @NotNull + @Override + public ElementBuilder cancelOnClick() { + return this.cancelOnClick(true); + } + + @NotNull + @Override + public ElementBuilder closeOnClick() { + return this.closeOnClick(true); + } + + @NotNull + @Override + public ElementBuilder updateOnClick() { + return this.updateOnClick(true); + } + + @NotNull + @Override + public ElementBuilder cancelOnClick(final boolean cancelOnClick) { + this.cancelOnClick = cancelOnClick; + return this; + } + + @NotNull + @Override + public ElementBuilder closeOnClick(final boolean cancelOnClick) { + this.closeOnClick = cancelOnClick; + return this; + } + + @NotNull + @Override + public ElementBuilder updateOnClick(final boolean updateOnClick) { + this.updateOnClick = updateOnClick; + return this; + } + + @NotNull + @Override + public ElementBuilder updateOnStateChange( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + if (this.updateOnStateChange == null) { + this.updateOnStateChange = new HashSet<>(); + } + this.updateOnStateChange.add(state); + Collections.addAll(this.updateOnStateChange, otherStates); + return this; + } + + @NotNull + @Override + public ElementBuilder updateOnStateAccess( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + if (this.updateOnStateAccess == null) { + this.updateOnStateAccess = new HashSet<>(); + } + this.updateOnStateAccess.add(state); + Collections.addAll(this.updateOnStateAccess, otherStates); + return this; + } + + @NotNull + @Override + public ElementBuilder displayIf(@NotNull final Predicate condition) { + this.displayIf = condition; + return this; + } + + @NotNull + @Override + public ElementBuilder displayIf(@NotNull final BooleanSupplier condition) { + return this.displayIf(__ -> condition.getAsBoolean()); + } + + @NotNull + @Override + public ElementBuilder hideIf(@NotNull final Predicate condition) { + return this.displayIf(ctx -> condition.negate().test(ctx)); + } + + @NotNull + @Override + public ElementBuilder hideIf(@NotNull final BooleanSupplier condition) { + return this.hideIf(__ -> condition.getAsBoolean()); + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementBuilderRich.java b/core/src/main/java/net/infumia/frame/element/ElementBuilderRich.java new file mode 100644 index 0000000..b71e739 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementBuilderRich.java @@ -0,0 +1,12 @@ +package net.infumia.frame.element; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementBuilderRich extends ElementBuilder { + @NotNull + ElementBuilder root(@NotNull Element root); + + @NotNull + Element build(@NotNull ContextBase parent); +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementEventHandler.java b/core/src/main/java/net/infumia/frame/element/ElementEventHandler.java new file mode 100644 index 0000000..61550f5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementEventHandler.java @@ -0,0 +1,24 @@ +package net.infumia.frame.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.service.ConsumerService; +import org.jetbrains.annotations.NotNull; + +public interface ElementEventHandler { + @NotNull + CompletableFuture handleRender( + @NotNull PipelineContextElement.Render ctx + ); + + @NotNull + CompletableFuture handleClear(@NotNull PipelineContextElement.Clear ctx); + + @NotNull + CompletableFuture handleClick(@NotNull PipelineContextElement.Click ctx); + + @NotNull + CompletableFuture handleUpdate( + @NotNull PipelineContextElement.Update ctx + ); +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementEventHandlerHolder.java b/core/src/main/java/net/infumia/frame/element/ElementEventHandlerHolder.java new file mode 100644 index 0000000..d2968a7 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementEventHandlerHolder.java @@ -0,0 +1,8 @@ +package net.infumia.frame.element; + +import org.jetbrains.annotations.NotNull; + +public interface ElementEventHandlerHolder { + @NotNull + ElementEventHandler eventHandler(); +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementEventHandlerItem.java b/core/src/main/java/net/infumia/frame/element/ElementEventHandlerItem.java new file mode 100644 index 0000000..e28cfaa --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementEventHandlerItem.java @@ -0,0 +1,172 @@ +package net.infumia.frame.element; + +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.context.element.ContextElementItemClick; +import net.infumia.frame.context.element.ContextElementItemClickImpl; +import net.infumia.frame.context.element.ContextElementItemRender; +import net.infumia.frame.context.element.ContextElementItemRenderImpl; +import net.infumia.frame.context.element.ContextElementItemUpdate; +import net.infumia.frame.context.element.ContextElementItemUpdateImpl; +import net.infumia.frame.context.element.ContextElementRender; +import net.infumia.frame.context.element.ContextElementUpdate; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.util.Preconditions; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +final class ElementEventHandlerItem implements ElementEventHandler { + + static final ElementEventHandler INSTANCE = new ElementEventHandlerItem(); + + private ElementEventHandlerItem() {} + + @NotNull + @Override + public CompletableFuture handleRender( + @NotNull final PipelineContextElement.Render ctx + ) { + final ContextElementRender context = ctx.context(); + final ElementItemRich element = (ElementItemRich) context.element(); + if (element.shouldRender(context)) { + this.forceRender(element, context); + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + return this.checkOverlapping(element, context); + } + + @NotNull + @Override + public CompletableFuture handleClear( + @NotNull final PipelineContextElement.Clear ctx + ) { + ctx.context().container().removeItem(((ElementItemRich) ctx.context().element()).slot()); + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + + @NotNull + @Override + public CompletableFuture handleClick( + @NotNull final PipelineContextElement.Click ctx + ) { + final ContextElementClick context = ctx.context(); + final ElementItemRich element = (ElementItemRich) context.element(); + final Consumer onClick = element.onClick(); + if (onClick != null) { + onClick.accept(new ContextElementItemClickImpl(context, element)); + } + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + + @NotNull + @Override + public CompletableFuture handleUpdate( + @NotNull final PipelineContextElement.Update ctx + ) { + final ContextElementUpdate context = ctx.context(); + final ElementItemRich element = (ElementItemRich) context.element(); + if (!context.forced() && element.displayIf() == null && element.onRender() == null) { + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + final Consumer onUpdate = element.onUpdate(); + if (element.visible() && onUpdate != null) { + onUpdate.accept(new ContextElementItemUpdateImpl(context, element)); + } + if (context.cancelled()) { + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + return element.pipelines().executeRender(context); + } + + @NotNull + private CompletableFuture checkOverlapping( + @NotNull final ElementRich compareTo, + @NotNull final ContextElementRender context + ) { + compareTo.visible(false); + final Optional overlappingOptional = + this.findOverlappingElement(compareTo, context); + if (!overlappingOptional.isPresent()) { + return compareTo.pipelines().executeClear(context); + } + final ElementRich overlapping = overlappingOptional.get(); + return overlapping + .pipelines() + .executeRender(context) + .thenCompose(__ -> { + if (overlapping.visible()) { + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } else { + return compareTo.pipelines().executeClear(context); + } + }); + } + + @NotNull + private Optional findOverlappingElement( + @NotNull final ElementRich compareTo, + @NotNull final ContextElementRender context + ) { + for (final Element child : context.elements()) { + final ElementRich element = (ElementRich) child; + if (!element.visible()) { + continue; + } + + if (Objects.equals(element.key(), compareTo.key())) { + continue; + } + + if (element instanceof ElementContainer) { + final ElementRich root = (ElementRich) compareTo.root(); + if (root != null && Objects.equals(element.key(), root.key())) { + continue; + } + for (final Element deepChild : ((ElementContainer) element).elements()) { + final ElementRich deepElement = (ElementRich) deepChild; + if (!deepElement.visible()) { + continue; + } + + if (Objects.equals(deepElement.key(), compareTo.key())) { + continue; + } + + if (deepElement.intersects(compareTo)) { + return Optional.of(deepElement); + } + } + continue; + } + + if (element.intersects(compareTo)) { + return Optional.of(element); + } + } + return Optional.empty(); + } + + private void forceRender( + @NotNull final ElementItemRich element, + @NotNull final ContextElementRender delegate + ) { + final ContextElementItemRender context = new ContextElementItemRenderImpl( + delegate, + element + ); + final Consumer onRender = element.onRender(); + if (onRender != null) { + onRender.accept(context); + } + final ItemStack modifiedItem = context.modifiedItem(); + final int modifiedSlot = context.modifiedSlot(); + // TODO: portlek, More detailed message. + Preconditions.state(modifiedSlot != -1, "Element's '%s' slot is not set!", element); + delegate.container().addItem(modifiedSlot, modifiedItem); + element.visible(true); + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementImpl.java b/core/src/main/java/net/infumia/frame/element/ElementImpl.java new file mode 100644 index 0000000..d7e285a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementImpl.java @@ -0,0 +1,129 @@ +package net.infumia.frame.element; + +import java.util.Collection; +import java.util.UUID; +import java.util.function.Predicate; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.executor.PipelineExecutorElement; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ElementImpl implements ElementRich { + + private final String key; + final ContextBase parent; + final Element root; + final boolean cancelOnClick; + final boolean updateOnClick; + final boolean closeOnClick; + final Predicate displayIf; + final Collection> updateOnStateChange; + final Collection> updateOnStateAccess; + private boolean visible = true; + + public ElementImpl( + @NotNull final ElementBuilderImpl builder, + @NotNull final ContextBase parent + ) { + this.key = UUID.randomUUID().toString(); + this.cancelOnClick = builder.cancelOnClick; + this.updateOnClick = builder.updateOnClick; + this.closeOnClick = builder.closeOnClick; + this.displayIf = builder.displayIf; + this.updateOnStateChange = builder.updateOnStateChange; + this.updateOnStateAccess = builder.updateOnStateAccess; + this.parent = parent; + this.root = builder.root; + } + + @NotNull + @Override + public ContextBase parent() { + return this.parent; + } + + @Nullable + @Override + public Element root() { + return this.root; + } + + @Override + public boolean visible() { + return this.visible; + } + + @Override + public void visible(final boolean visible) { + this.visible = visible; + } + + @Override + public boolean shouldRender(@NotNull final ContextRender context) { + return this.displayIf == null || this.displayIf.test(context); + } + + @Override + public boolean containedWithin(final int slot) { + return false; + } + + @Override + public boolean intersects(@NotNull final Element element) { + return false; + } + + @NotNull + @Override + public ElementBuilder toBuilder() { + return new ElementBuilderImpl(this); + } + + @Override + public boolean cancelOnClick() { + return this.cancelOnClick; + } + + @Override + public boolean closeOnClick() { + return this.closeOnClick; + } + + @Override + public boolean updateOnClick() { + return this.updateOnClick; + } + + @Nullable + @Override + public Predicate displayIf() { + return this.displayIf; + } + + @Nullable + @Override + public Collection> updateOnStateChange() { + return this.updateOnStateChange; + } + + @Nullable + @Override + public Collection> updateOnStateAccess() { + return this.updateOnStateAccess; + } + + @Override + public String key() { + return this.key; + } + + @NotNull + @Override + public PipelineExecutorElement pipelines() { + throw new UnsupportedOperationException( + "This element does not have a pipeline implementation!" + ); + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementItemBuilderImpl.java b/core/src/main/java/net/infumia/frame/element/ElementItemBuilderImpl.java new file mode 100644 index 0000000..587ca51 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementItemBuilderImpl.java @@ -0,0 +1,198 @@ +package net.infumia.frame.element; + +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.element.ContextElementItemClick; +import net.infumia.frame.context.element.ContextElementItemRender; +import net.infumia.frame.context.element.ContextElementItemUpdate; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.state.State; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ElementItemBuilderImpl + extends ElementBuilderImpl + implements ElementItemBuilderRich { + + ItemStack item; + int slot = -1; + Consumer onClick; + Consumer onRender; + Consumer onUpdate; + + ElementItemBuilderImpl(@NotNull final ElementItemImpl element) { + super(element); + this.item = element.item; + this.slot = element.slot; + this.onClick = element.onClick; + this.onRender = element.onRender; + this.onUpdate = element.onUpdate; + } + + public ElementItemBuilderImpl() {} + + @Override + public int slot() { + return this.slot; + } + + @NotNull + @Override + public ElementItemBuilderImpl item(@Nullable final ItemStack item) { + this.item = item; + return this; + } + + @NotNull + @Override + public ElementItemBuilderImpl slot(final int slot) { + this.slot = slot; + return this; + } + + @NotNull + @Override + public ElementItemBuilder onClick(@NotNull final Consumer onClick) { + this.onClick = onClick; + return this; + } + + @NotNull + @Override + public ElementItemBuilder onClick(@NotNull final Runnable onClick) { + return this.onClick(__ -> onClick.run()); + } + + @NotNull + @Override + public ElementItemBuilderImpl onRender( + @Nullable final Consumer onRender + ) { + this.onRender = onRender; + return this; + } + + @NotNull + @Override + public ElementItemBuilder renderWith( + @NotNull final Function renderWith + ) { + return this.onRender(ctx -> ctx.modifyItem(renderWith.apply(ctx))); + } + + @NotNull + @Override + public ElementItemBuilder onUpdate( + @Nullable final Consumer onUpdate + ) { + this.onUpdate = onUpdate; + return this; + } + + @NotNull + @Override + public ElementItemBuilder root(@NotNull final Element root) { + super.root(root); + return this; + } + + @NotNull + @Override + public ElementItem build(@NotNull final ContextBase parent) { + return new ElementItemImpl(this, parent); + } + + @NotNull + @Override + public ElementItemBuilder cancelOnClick() { + super.cancelOnClick(); + return this; + } + + @NotNull + @Override + public ElementItemBuilder closeOnClick() { + super.closeOnClick(); + return this; + } + + @NotNull + @Override + public ElementItemBuilder updateOnClick() { + super.updateOnClick(); + return this; + } + + @NotNull + @Override + public ElementItemBuilder cancelOnClick(final boolean cancelOnClick) { + super.cancelOnClick(cancelOnClick); + return this; + } + + @NotNull + @Override + public ElementItemBuilder closeOnClick(final boolean cancelOnClick) { + super.closeOnClick(cancelOnClick); + return this; + } + + @NotNull + @Override + public ElementItemBuilder updateOnClick(final boolean updateOnClick) { + super.updateOnClick(updateOnClick); + return this; + } + + @NotNull + @Override + public ElementItemBuilder updateOnStateChange( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + super.updateOnStateChange(state, otherStates); + return this; + } + + @NotNull + @Override + public ElementItemBuilder updateOnStateAccess( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + super.updateOnStateAccess(state, otherStates); + return this; + } + + @NotNull + @Override + public ElementItemBuilder displayIf(@NotNull final Predicate condition) { + super.displayIf(condition); + return this; + } + + @NotNull + @Override + public ElementItemBuilder displayIf(@NotNull final BooleanSupplier condition) { + super.displayIf(condition); + return this; + } + + @NotNull + @Override + public ElementItemBuilder hideIf(@NotNull final Predicate condition) { + super.hideIf(condition); + return this; + } + + @NotNull + @Override + public ElementItemBuilder hideIf(@NotNull final BooleanSupplier condition) { + super.hideIf(condition); + return this; + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementItemBuilderRich.java b/core/src/main/java/net/infumia/frame/element/ElementItemBuilderRich.java new file mode 100644 index 0000000..2781276 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementItemBuilderRich.java @@ -0,0 +1,12 @@ +package net.infumia.frame.element; + +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface ElementItemBuilderRich extends ElementBuilderRich, ElementItemBuilder { + @NotNull + @Override + ElementItem build(@NotNull ContextBase parent); + + int slot(); +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementItemImpl.java b/core/src/main/java/net/infumia/frame/element/ElementItemImpl.java new file mode 100644 index 0000000..7907ace --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementItemImpl.java @@ -0,0 +1,96 @@ +package net.infumia.frame.element; + +import java.util.function.Consumer; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.element.ContextElementItemClick; +import net.infumia.frame.context.element.ContextElementItemRender; +import net.infumia.frame.context.element.ContextElementItemUpdate; +import net.infumia.frame.pipeline.executor.PipelineExecutorElement; +import net.infumia.frame.pipeline.executor.PipelineExecutorElementImpl; +import net.infumia.frame.util.Preconditions; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ElementItemImpl extends ElementImpl implements ElementItemRich { + + private final PipelineExecutorElement pipelines = new PipelineExecutorElementImpl(this); + private final ElementEventHandler eventHandler = ElementEventHandlerItem.INSTANCE; + final ItemStack item; + final int slot; + final Consumer onClick; + final Consumer onRender; + final Consumer onUpdate; + + ElementItemImpl( + @NotNull final ElementItemBuilderImpl builder, + @NotNull final ContextBase parent + ) { + super(builder, parent); + this.item = Preconditions.argumentNotNull(builder.item, "Item is not set!"); + this.slot = builder.slot; + this.onClick = builder.onClick; + this.onRender = builder.onRender; + this.onUpdate = builder.onUpdate; + } + + @NotNull + @Override + public ItemStack item() { + return this.item; + } + + @Override + public int slot() { + return this.slot; + } + + @Nullable + @Override + public Consumer onClick() { + return this.onClick; + } + + @Nullable + @Override + public Consumer onRender() { + return this.onRender; + } + + @Nullable + @Override + public Consumer onUpdate() { + return this.onUpdate; + } + + @NotNull + @Override + public ElementEventHandler eventHandler() { + return this.eventHandler; + } + + @Override + public boolean containedWithin(final int slot) { + return this.slot == slot; + } + + @Override + public boolean intersects(@NotNull final Element element) { + if (!(element instanceof ElementItem)) { + return false; + } + return ((ElementItem) element).slot() == this.slot; + } + + @NotNull + @Override + public ElementItemBuilderRich toBuilder() { + return new ElementItemBuilderImpl(this); + } + + @NotNull + @Override + public PipelineExecutorElement pipelines() { + return this.pipelines; + } +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementItemRich.java b/core/src/main/java/net/infumia/frame/element/ElementItemRich.java new file mode 100644 index 0000000..6cb7200 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementItemRich.java @@ -0,0 +1,9 @@ +package net.infumia.frame.element; + +import org.jetbrains.annotations.NotNull; + +public interface ElementItemRich extends ElementRich, ElementItem, ElementEventHandlerHolder { + @NotNull + @Override + ElementItemBuilder toBuilder(); +} diff --git a/core/src/main/java/net/infumia/frame/element/ElementRich.java b/core/src/main/java/net/infumia/frame/element/ElementRich.java new file mode 100644 index 0000000..decb004 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/ElementRich.java @@ -0,0 +1,30 @@ +package net.infumia.frame.element; + +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.Pipelined; +import net.infumia.frame.pipeline.executor.PipelineExecutorElement; +import net.infumia.frame.util.Keyed; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ElementRich extends Element, Pipelined, Keyed { + @NotNull + ContextBase parent(); + + @Nullable + Element root(); + + boolean visible(); + + void visible(boolean visible); + + boolean shouldRender(@NotNull ContextRender context); + + boolean containedWithin(int slot); + + boolean intersects(@NotNull Element element); + + @NotNull + ElementBuilder toBuilder(); +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/ElementEventHandlerPagination.java b/core/src/main/java/net/infumia/frame/element/pagination/ElementEventHandlerPagination.java new file mode 100644 index 0000000..da9f0ef --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/ElementEventHandlerPagination.java @@ -0,0 +1,150 @@ +package net.infumia.frame.element.pagination; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.element.ContextElementClear; +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.context.element.ContextElementRender; +import net.infumia.frame.context.element.ContextElementUpdate; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandler; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.state.StateRich; +import org.jetbrains.annotations.NotNull; + +final class ElementEventHandlerPagination implements ElementEventHandler { + + static final ElementEventHandler INSTANCE = new ElementEventHandlerPagination(); + + private ElementEventHandlerPagination() {} + + @NotNull + @Override + public CompletableFuture handleRender( + @NotNull final PipelineContextElement.Render ctx + ) { + final ContextElementRender context = ctx.context(); + final ElementPaginationRich pagination = (ElementPaginationRich) context.element(); + if (pagination.initialized() && !pagination.pageWasChanged()) { + pagination.visible(true); + return this.renderChild(context, pagination); + } + if (!pagination.initialized()) { + pagination.updatePageSize(context); + } + return pagination + .loadCurrentPage(context) + .thenCompose(__ -> { + pagination.visible(true); + pagination.initialized(true); + return this.renderChild(context, pagination) + .thenCompose(s -> + ((StateRich) pagination.associated()).manualUpdateWait( + context + ) + ) + .thenApply(v -> ConsumerService.State.CONTINUE); + }); + } + + @NotNull + @Override + public CompletableFuture handleClear( + @NotNull final PipelineContextElement.Clear ctx + ) { + final ContextElementClear context = ctx.context(); + final ElementPaginationRich pagination = (ElementPaginationRich) context.element(); + if (!pagination.pageWasChanged()) { + final List elements = pagination.elements(); + final CompletableFuture[] futures = new CompletableFuture[elements.size()]; + for (int i = 0; i < futures.length; i++) { + futures[i] = ((ElementRich) elements.get(i)).pipelines().executeClear(context); + } + return CompletableFuture.allOf(futures).thenApply(__ -> ConsumerService.State.CONTINUE); + } + final Collection elements = pagination.modifiableElements(); + final Collection> futures = new ArrayList<>(); + final Iterator iterator = elements.iterator(); + while (iterator.hasNext()) { + final Element element = iterator.next(); + futures.add(((ElementRich) element).pipelines().executeClear(context)); + iterator.remove(); + } + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApply(__ -> + ConsumerService.State.CONTINUE + ); + } + + @NotNull + @Override + public CompletableFuture handleClick( + @NotNull final PipelineContextElement.Click ctx + ) { + final ContextElementClick context = ctx.context(); + final ElementPaginationRich pagination = (ElementPaginationRich) context.element(); + if (pagination.pageWasChanged() || !pagination.visible()) { + ctx.cancelled(true); + context.cancelled(true); + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + for (final Element child : pagination.elements()) { + final ElementRich element = (ElementRich) child; + if (!element.containedWithin(context.clickedSlotRaw()) || !element.visible()) { + continue; + } + return element.pipelines().executeClick(context); + } + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + + @NotNull + @Override + public CompletableFuture handleUpdate( + @NotNull final PipelineContextElement.Update ctx + ) { + final ContextElementUpdate context = ctx.context(); + final ElementPaginationRich pagination = (ElementPaginationRich) context.element(); + if (pagination.pageWasChanged()) { + return pagination + .pipelines() + .executeClear(context) + .thenCompose(__ -> { + pagination.clearElements(); + return pagination.pipelines().executeRender(context); + }) + .thenApply(__ -> { + pagination.pageWasChanged(false); + return ConsumerService.State.CONTINUE; + }); + } + if (!pagination.visible()) { + return CompletableFuture.completedFuture(ConsumerService.State.CONTINUE); + } + final List elements = pagination.elements(); + final CompletableFuture[] futures = new CompletableFuture[elements.size()]; + for (int i = 0; i < futures.length; i++) { + futures[i] = ((ElementRich) elements.get(i)).pipelines() + .executeUpdate(context, context.forced()); + } + return CompletableFuture.allOf(futures).thenApply(__ -> ConsumerService.State.CONTINUE); + } + + @NotNull + private CompletableFuture renderChild( + @NotNull final ContextElementRender context, + @NotNull final ElementPaginationRich pagination + ) { + final List elements = pagination.elements(); + final CompletableFuture[] futures = new CompletableFuture[elements.size()]; + for (int i = 0; i < futures.length; i++) { + final ElementRich element = (ElementRich) elements.get(i); + futures[i] = element.pipelines().executeRender(context); + } + return CompletableFuture.allOf(futures).thenApply(__ -> ConsumerService.State.CONTINUE); + } +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderImpl.java b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderImpl.java new file mode 100644 index 0000000..962b2c8 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderImpl.java @@ -0,0 +1,197 @@ +package net.infumia.frame.element.pagination; + +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementBuilderImpl; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.state.State; +import net.infumia.frame.state.pagination.ElementConfigurer; +import net.infumia.frame.state.pagination.StatePagination; +import org.jetbrains.annotations.NotNull; + +public final class ElementPaginationBuilderImpl + extends ElementBuilderImpl + implements ElementPaginationBuilderRich { + + final SourceProvider sourceProvider; + final Function, StatePagination> stateFactory; + char layout = '0'; + BiConsumer onPageSwitch; + ElementConfigurer elementConfigurer; + State associated; + + public ElementPaginationBuilderImpl( + @NotNull final SourceProvider sourceProvider, + @NotNull final Function, StatePagination> stateFactory + ) { + this.sourceProvider = sourceProvider; + this.stateFactory = stateFactory; + } + + ElementPaginationBuilderImpl(@NotNull final ElementPaginationImpl element) { + super(element); + this.sourceProvider = element.sourceProvider; + this.stateFactory = element.stateFactory; + this.layout = element.layout; + this.onPageSwitch = element.onPageSwitch; + this.elementConfigurer = element.elementConfigurer; + } + + @NotNull + @Override + public ElementPaginationBuilderRich associated( + @NotNull final State associated + ) { + this.associated = associated; + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder layout(final char layout) { + this.layout = layout; + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder onPageSwitch( + @NotNull final BiConsumer onPageSwitch + ) { + this.onPageSwitch = onPageSwitch; + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder elementConfigurer( + @NotNull final BiConsumer configurer + ) { + return this.elementConfigurer((__, b, ___, ____, value) -> configurer.accept(b, value)); + } + + @NotNull + @Override + public ElementPaginationBuilder elementConfigurer( + @NotNull final ElementConfigurer configurer + ) { + this.elementConfigurer = configurer; + return this; + } + + @NotNull + @Override + public StatePagination buildPagination() { + return this.stateFactory.apply(this); + } + + @NotNull + @Override + public ElementPaginationBuilder root(@NotNull final Element root) { + super.root(root); + return this; + } + + @NotNull + @Override + public ElementPagination build(@NotNull final ContextBase parent) { + return new ElementPaginationImpl<>(this, parent); + } + + @NotNull + @Override + public ElementPaginationBuilder cancelOnClick() { + super.cancelOnClick(); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder closeOnClick() { + super.closeOnClick(); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder updateOnClick() { + super.updateOnClick(); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder cancelOnClick(final boolean cancelOnClick) { + super.cancelOnClick(cancelOnClick); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder closeOnClick(final boolean cancelOnClick) { + super.closeOnClick(cancelOnClick); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder updateOnClick(final boolean updateOnClick) { + super.updateOnClick(updateOnClick); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder updateOnStateChange( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + super.updateOnStateChange(state, otherStates); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder updateOnStateAccess( + @NotNull final State state, + @NotNull final State @NotNull... otherStates + ) { + super.updateOnStateAccess(state, otherStates); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder displayIf( + @NotNull final Predicate condition + ) { + super.displayIf(condition); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder displayIf(@NotNull final BooleanSupplier condition) { + super.displayIf(condition); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder hideIf(@NotNull final Predicate condition) { + super.hideIf(condition); + return this; + } + + @NotNull + @Override + public ElementPaginationBuilder hideIf(@NotNull final BooleanSupplier condition) { + super.hideIf(condition); + return this; + } +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderRich.java b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderRich.java new file mode 100644 index 0000000..f0af7d4 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationBuilderRich.java @@ -0,0 +1,16 @@ +package net.infumia.frame.element.pagination; + +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.element.ElementBuilderRich; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; + +public interface ElementPaginationBuilderRich + extends ElementBuilderRich, ElementPaginationBuilder { + @NotNull + ElementPaginationBuilderRich associated(@NotNull State associated); + + @NotNull + @Override + ElementPagination build(@NotNull ContextBase parent); +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationImpl.java b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationImpl.java new file mode 100644 index 0000000..ad2aefb --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationImpl.java @@ -0,0 +1,491 @@ +package net.infumia.frame.element.pagination; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.BiConsumer; +import java.util.function.Function; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandler; +import net.infumia.frame.element.ElementImpl; +import net.infumia.frame.element.ElementItem; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.element.ElementItemBuilderImpl; +import net.infumia.frame.element.ElementItemBuilderRich; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.pipeline.executor.PipelineExecutorElement; +import net.infumia.frame.pipeline.executor.PipelineExecutorElementImpl; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.state.State; +import net.infumia.frame.state.StateRich; +import net.infumia.frame.state.pagination.ElementConfigurer; +import net.infumia.frame.state.pagination.StatePagination; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.ViewContainer; +import org.jetbrains.annotations.NotNull; + +public final class ElementPaginationImpl + extends ElementImpl + implements ElementPaginationRich { + + private final ReadWriteLock elementLock = new ReentrantReadWriteLock(); + private final PipelineExecutorElement pipelines = new PipelineExecutorElementImpl(this); + private final ElementEventHandler eventHandler = ElementEventHandlerPagination.INSTANCE; + private LayoutSlot currentLayoutSlot; + final SourceProvider sourceProvider; + final Function, StatePagination> stateFactory; + final char layout; + final BiConsumer onPageSwitch; + final ElementConfigurer elementConfigurer; + final State associated; + private final Function>> sourceFactory; + private List elements = new ArrayList<>(); + private int currentPageIndex = 0; + private boolean pageWasChanged; + private boolean initialized = false; + private int pageCount; + private List currentSource; + private boolean loading; + private int pageSize = -1; + + ElementPaginationImpl( + @NotNull final ElementPaginationBuilderImpl builder, + @NotNull final ContextBase parent + ) { + super(builder, parent); + this.associated = Preconditions.argumentNotNull( + builder.associated, + "Associated state cannot be null for ElementPagination!" + ); + this.elementConfigurer = Preconditions.argumentNotNull( + builder.elementConfigurer, + "Element configurer cannot be null for ElementPagination!" + ); + this.sourceProvider = builder.sourceProvider; + this.stateFactory = builder.stateFactory; + this.layout = builder.layout; + this.onPageSwitch = builder.onPageSwitch; + + if (this.sourceProvider instanceof SourceProvider.Immutable) { + this.currentSource = this.sourceProvider.apply(this.parent()).join(); + this.sourceFactory = null; + } else { + this.sourceFactory = this.sourceProvider; + } + } + + @NotNull + @Override + public State associated() { + return this.associated; + } + + @Override + public boolean pageWasChanged() { + return this.pageWasChanged; + } + + @Override + public void pageWasChanged(final boolean pageWasChanged) { + this.pageWasChanged = pageWasChanged; + } + + @Override + public boolean initialized() { + return this.initialized; + } + + @Override + public void initialized(final boolean initialized) { + this.initialized = initialized; + } + + @Override + public void updatePageSize(@NotNull final ContextRender context) { + final String[] layout = context.config().layout(); + if (layout == null) { + this.pageSize = context.container().size(); + } else { + this.pageSize = this.layoutSlotFor(context).slots().length; + } + } + + @NotNull + @Override + public CompletableFuture loadCurrentPage(@NotNull final ContextRender context) { + return this.loadSourceForTheCurrentPage(context).thenAccept(pageContents -> { + if (pageContents.isEmpty()) { + return; + } + if (context.layouts().isEmpty()) { + this.addComponentsForUnconstrainedPagination(context, pageContents); + } else { + this.addComponentsForLayeredPagination(context, pageContents); + } + }); + } + + @NotNull + @Override + public Collection modifiableElements() { + try { + this.elementLock.readLock().lock(); + return this.elements; + } finally { + this.elementLock.readLock().unlock(); + } + } + + @Override + public void clearElements() { + try { + this.elementLock.writeLock().lock(); + this.elements = new ArrayList<>(); + } finally { + this.elementLock.writeLock().unlock(); + } + } + + @Override + public int currentPageIndex() { + return this.currentPageIndex; + } + + @Override + public int nextPageIndex() { + return Math.max(0, Math.min(this.pageCount, this.currentPageIndex + 1)); + } + + @Override + public int previousPageIndex() { + return Math.max(0, Math.min(this.pageCount, this.currentPageIndex - 1)); + } + + @Override + public int lastPageIndex() { + return Math.max(0, this.pageCount - 1); + } + + @Override + public boolean isFirstPage() { + return this.currentPageIndex == 0; + } + + @Override + public boolean isLastPage() { + return !this.canAdvance(); + } + + @Override + public int elementCount() { + return this.currentSource == null ? 0 : this.currentSource.size(); + } + + @Override + public int pageCount() { + return this.pageCount; + } + + @Override + public boolean hasPage(final int pageIndex) { + if (this.sourceProvider.computed()) { + return true; + } else if (pageIndex < 0) { + return false; + } else { + return pageIndex < this.pageCount; + } + } + + @Override + public void switchTo(final int pageIndex) { + Preconditions.argumentNotNull( + this.hasPage(pageIndex), + "Page index not found (%d > %d)", + pageIndex, + this.pageCount + ); + if (this.loading) { + return; + } + this.currentPageIndex = pageIndex; + this.pageWasChanged = true; + final ContextRender host = (ContextRender) this.parent(); + if (this.onPageSwitch != null) { + this.onPageSwitch.accept(host, this); + } + CompletableFutureExtensions.logError( + this.pipelines.executeUpdate(host, false), + this.parent().manager().logger(), + "An error occurred while updating the pagination '%s'.", + this + ); + } + + @Override + public void advance() { + if (this.canAdvance()) { + this.switchTo(this.currentPageIndex + 1); + } + } + + @Override + public boolean canAdvance() { + return this.hasPage(this.currentPageIndex + 1); + } + + @Override + public void back() { + if (this.canBack()) { + this.switchTo(this.currentPageIndex - 1); + } + } + + @Override + public boolean canBack() { + return this.hasPage(this.currentPageIndex - 1); + } + + @NotNull + @Override + public ElementEventHandler eventHandler() { + return this.eventHandler; + } + + @Override + public void visible(final boolean visible) { + try { + this.elementLock.readLock().lock(); + super.visible(visible); + for (final Element child : this.elements) { + ((ElementRich) child).visible(visible); + } + } finally { + this.elementLock.readLock().unlock(); + } + } + + @Override + public boolean containedWithin(final int position) { + try { + this.elementLock.readLock().lock(); + if (this.currentLayoutSlot != null) { + return Arrays.stream(this.currentLayoutSlot.slots()).anyMatch( + slot -> slot == position + ); + } + return this.elements.stream() + .anyMatch(element -> ((ElementRich) element).containedWithin(position)); + } finally { + this.elementLock.readLock().unlock(); + } + } + + @Override + public boolean intersects(@NotNull final Element element) { + try { + this.elementLock.readLock().lock(); + for (final Element child : this.elements) { + final ElementRich e = (ElementRich) child; + if (e.intersects(element)) { + return true; + } + if (e instanceof ElementItem) { + final int slot = ((ElementItem) e).slot(); + if (this.currentLayoutSlot != null) { + return Arrays.stream(this.currentLayoutSlot.slots()).anyMatch( + s -> s == slot + ); + } + return e.containedWithin(slot); + } + } + return false; + } finally { + this.elementLock.readLock().unlock(); + } + } + + @NotNull + @Override + public ElementPaginationBuilderRich toBuilder() { + return new ElementPaginationBuilderImpl<>(this); + } + + @NotNull + @Override + public List elements() { + try { + this.elementLock.readLock().lock(); + return Collections.unmodifiableList(this.elements); + } finally { + this.elementLock.readLock().unlock(); + } + } + + private void addComponentsForUnconstrainedPagination( + @NotNull final ContextRender context, + @NotNull final List contents + ) { + final ViewContainer container = context.container(); + + /*if (this.pageSize == -1) { + this.updatePageSize(context); + }*/ + + final int lastSlot = Math.min(container.lastSlot() + 1, contents.size()); + for (int i = container.firstSlot(); i < lastSlot; i++) { + final T value = contents.get(i); + final ElementItemBuilder builder = new ElementItemBuilderImpl().slot(i).root(this); + this.elementConfigurer.configure(context, builder, i, i, value); + this.elements.add(((ElementItemBuilderRich) builder).build(context)); + } + } + + private void addComponentsForLayeredPagination( + @NotNull final ContextRender context, + @NotNull final List contents + ) { + final LayoutSlot layoutSLot = this.layoutSlotFor(context); + final int elementCount = contents.size(); + int index = 0; + for (final int slot : layoutSLot.slots()) { + final T value = contents.get(index++); + final ElementItemBuilder builder = new ElementItemBuilderImpl().slot(slot).root(this); + this.elementConfigurer.configure(context, builder, index, slot, value); + this.elements.add(((ElementItemBuilderRich) builder).build(context)); + if (index == elementCount) { + break; + } + } + } + + @NotNull + private CompletableFuture> loadSourceForTheCurrentPage( + @NotNull final ContextBase context + ) { + final boolean isLazy = this.sourceProvider.lazy(); + final boolean reuseLazy = isLazy && this.initialized; + final boolean force = false; // TODO: portlek, Implement this. + if ( + (this.sourceProvider.provided() || reuseLazy) && + !this.sourceProvider.computed() && + !force + ) { + final List currentSource = Preconditions.stateNotNull( + this.currentSource, + "Current source cannot be null in this stage" + ); + + if (!isLazy) { + this.pageCount = this.calculatePagesCount(currentSource); + } + + return CompletableFuture.completedFuture( + ElementPaginationImpl.splitSourceForPage( + this.currentPageIndex, + this.pageSize(), + this.pageCount, + currentSource + ) + ); + } + + this.loading = true; + return ((StateRich) this.associated).manualUpdateWait( + context + ).thenCompose(__ -> { + if (this.sourceFactory == null) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + return this.sourceFactory.apply(context).thenCompose(result -> { + this.currentSource = result; + this.pageCount = this.calculatePagesCount(result); + this.loading = false; + return ((StateRich) this.associated).manualUpdateWait( + context + ).thenApply(value -> + !isLazy + ? result + : ElementPaginationImpl.splitSourceForPage( + this.currentPageIndex, + this.pageSize(), + this.pageCount, + result + ) + ); + }); + }); + } + + private int calculatePagesCount(@NotNull final List source) { + return (int) Math.ceil((double) source.size() / this.pageSize()); + } + + private int pageSize() { + Preconditions.state( + this.pageSize != -1, + "Page size need to be updated before try to get it" + ); + return this.pageSize; + } + + @NotNull + private LayoutSlot layoutSlotFor(@NotNull final ContextRender context) { + if (this.currentLayoutSlot != null) { + return this.currentLayoutSlot; + } + final LayoutSlot layoutSlot = Preconditions.argumentNotNull( + context.layouts().get(this.layout), + "Layout slot target not found: %c", + this.layout + ); + return this.currentLayoutSlot = layoutSlot; + } + + @NotNull + private static List splitSourceForPage( + final int index, + final int pageSize, + final int pagesCount, + @NotNull final List src + ) { + if (src.isEmpty()) { + return Collections.emptyList(); + } + if (src.size() <= pageSize) { + return new ArrayList<>(src); + } + if (index < 0 || (pagesCount > 0 && index > pagesCount)) { + throw new IndexOutOfBoundsException( + String.format( + "Page index must be between the range of 0 and %d. Given: %d", + pagesCount - 1, + index + ) + ); + } + + final List contents = new LinkedList<>(); + final int base = index * pageSize; + int until = base + pageSize; + if (until > src.size()) { + until = src.size(); + } + + for (int i = base; i < until; i++) { + contents.add(src.get(i)); + } + + return contents; + } +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationRich.java b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationRich.java new file mode 100644 index 0000000..28876f2 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/ElementPaginationRich.java @@ -0,0 +1,38 @@ +package net.infumia.frame.element.pagination; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandlerHolder; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.state.State; +import org.jetbrains.annotations.NotNull; + +public interface ElementPaginationRich + extends ElementRich, ElementPagination, ElementEventHandlerHolder { + @NotNull + State associated(); + + boolean pageWasChanged(); + + void pageWasChanged(boolean pageWasChanged); + + boolean initialized(); + + void initialized(boolean initialized); + + void updatePageSize(@NotNull ContextRender context); + + @NotNull + CompletableFuture loadCurrentPage(@NotNull ContextRender context); + + @NotNull + Collection modifiableElements(); + + void clearElements(); + + @NotNull + @Override + ElementPaginationBuilder toBuilder(); +} diff --git a/core/src/main/java/net/infumia/frame/element/pagination/SourceProvider.java b/core/src/main/java/net/infumia/frame/element/pagination/SourceProvider.java new file mode 100644 index 0000000..e7d3ed9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/element/pagination/SourceProvider.java @@ -0,0 +1,92 @@ +package net.infumia.frame.element.pagination; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; +import net.infumia.frame.context.ContextBase; +import org.jetbrains.annotations.NotNull; + +public interface SourceProvider extends Function>> { + boolean lazy(); + + boolean computed(); + + boolean provided(); + + final class Immutable implements SourceProvider { + + private final List values; + + public Immutable(@NotNull final List values) { + this.values = values; + } + + @NotNull + @Override + public CompletableFuture> apply(@NotNull final ContextBase context) { + return CompletableFuture.completedFuture(this.values); + } + + @Override + public boolean lazy() { + return false; + } + + @Override + public boolean computed() { + return false; + } + + @Override + public boolean provided() { + return true; + } + } + + final class Computed implements SourceProvider { + + private final Function>> provider; + private final boolean computed; + private final boolean lazy; + + public Computed( + @NotNull final Function>> provider, + final boolean computed, + final boolean lazy + ) { + this.provider = provider; + this.computed = computed; + this.lazy = lazy; + } + + public Computed( + @NotNull final Supplier>> provider, + final boolean computed, + final boolean lazy + ) { + this(base -> provider.get(), computed, lazy); + } + + @Override + public boolean lazy() { + return this.lazy; + } + + @Override + public boolean computed() { + return this.computed; + } + + @Override + public boolean provided() { + return false; + } + + @NotNull + @Override + public CompletableFuture> apply(@NotNull final ContextBase context) { + return this.provider.apply(context); + } + } +} diff --git a/core/src/main/java/net/infumia/frame/extension/CompletableFutureExtensions.java b/core/src/main/java/net/infumia/frame/extension/CompletableFutureExtensions.java new file mode 100644 index 0000000..7913378 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/extension/CompletableFutureExtensions.java @@ -0,0 +1,29 @@ +package net.infumia.frame.extension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import net.infumia.frame.logger.Logger; +import org.jetbrains.annotations.NotNull; + +public final class CompletableFutureExtensions { + + @NotNull + public static CompletableFuture logError( + @NotNull final CompletableFuture future, + @NotNull final Logger logger, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + return future.exceptionally(throwable -> { + if (throwable instanceof CompletionException) { + throwable = throwable.getCause(); + } + if (throwable instanceof StackOverflowError) { + throwable.printStackTrace(); + } else { + logger.error(throwable, message, args); + } + return null; + }); + } +} diff --git a/core/src/main/java/net/infumia/frame/listener/InventoryListener.java b/core/src/main/java/net/infumia/frame/listener/InventoryListener.java new file mode 100644 index 0000000..471ef3a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/listener/InventoryListener.java @@ -0,0 +1,125 @@ +package net.infumia.frame.listener; + +import java.util.function.Consumer; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.logger.Logger; +import net.infumia.frame.metadata.MetadataAccessFactory; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.view.ViewEventHandler; +import net.infumia.frame.viewer.ContextualViewer; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.metadata.Metadatable; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public final class InventoryListener implements Listener { + + private final Plugin plugin; + private final Logger logger; + private final MetadataAccessFactory metadataAccessFactory; + private final Runnable onUnregister; + + public InventoryListener( + @NotNull final Plugin plugin, + @NotNull final Logger logger, + @NotNull final MetadataAccessFactory metadataAccessFactory, + @NotNull final Runnable onUnregister + ) { + this.plugin = plugin; + this.logger = logger; + this.metadataAccessFactory = metadataAccessFactory; + this.onUnregister = onUnregister; + } + + public void register() { + Bukkit.getPluginManager().registerEvents(this, this.plugin); + } + + @EventHandler + public void onPluginDisable(final PluginDisableEvent event) { + if (event.getPlugin().getName().equals(this.plugin.getName())) { + this.onUnregister.run(); + } + } + + @EventHandler(ignoreCancelled = true) + public void onInventoryClick(final InventoryClickEvent event) { + this.ifContextualViewer(event.getWhoClicked(), viewer -> + CompletableFutureExtensions.logError( + ((ViewEventHandler) viewer.view()).simulateClick(viewer, event), + this.logger, + "Error occurred while viewer '%s' clicks an inventory!", + viewer + ) + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInventoryClose(final InventoryCloseEvent event) { + this.transitioningOrCurrent(event.getPlayer(), viewer -> + CompletableFutureExtensions.logError( + ((ViewEventHandler) viewer.view()).simulateClose(viewer), + this.logger, + "Error occurred while viewer '%s' closes an inventory", + viewer + ) + ); + } + + @EventHandler(ignoreCancelled = true) + public void onItemPickup(final PlayerPickupItemEvent event) { + this.ifContextualViewer(event.getPlayer(), viewer -> + ((ViewEventHandler) viewer.view()).handleItemPickup(viewer, event) + ); + } + + @EventHandler(ignoreCancelled = true) + public void onItemDrop(final PlayerDropItemEvent event) { + this.ifContextualViewer(event.getPlayer(), viewer -> + ((ViewEventHandler) viewer.view()).handleItemDrop(viewer, event) + ); + } + + @EventHandler(ignoreCancelled = true) + public void onInventoryDrag(final InventoryDragEvent event) { + this.ifContextualViewer(event.getWhoClicked(), viewer -> + ((ViewEventHandler) viewer.view()).handleInventoryDrag(viewer, event) + ); + } + + private void ifContextualViewer( + @NotNull final Metadatable metadatable, + @NotNull final Consumer consumer + ) { + final ContextualViewer viewer = + this.metadataAccessFactory.getOrCreate(metadatable).get( + MetadataKeyHolder.CONTEXTUAL_VIEWER + ); + if (viewer != null) { + consumer.accept(viewer); + } + } + + private void transitioningOrCurrent( + @NotNull final Metadatable metadatable, + @NotNull final Consumer consumer + ) { + final ContextualViewer transitioningFrom = + this.metadataAccessFactory.getOrCreate(metadatable).get( + MetadataKeyHolder.TRANSITIONING_FROM + ); + if (transitioningFrom == null) { + this.ifContextualViewer(metadatable, consumer); + } else { + consumer.accept(transitioningFrom); + } + } +} diff --git a/core/src/main/java/net/infumia/frame/logger/PluginLogger.java b/core/src/main/java/net/infumia/frame/logger/PluginLogger.java new file mode 100644 index 0000000..ce39aaf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/logger/PluginLogger.java @@ -0,0 +1,75 @@ +package net.infumia.frame.logger; + +import java.util.logging.Level; +import net.infumia.frame.util.PaperLib; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public final class PluginLogger implements Logger { + + @NotNull + private final Plugin plugin; + + private boolean debug = false; + + public PluginLogger(@NotNull final Plugin plugin) { + this.plugin = plugin; + } + + @Override + public synchronized boolean isDebugEnabled() { + return this.debug; + } + + @Override + public synchronized void enableDebug(final boolean enable) { + this.debug = enable; + } + + @Override + public void error( + @NotNull final Throwable throwable, + @NotNull final String message, + @NotNull final Object @NotNull... args + ) { + if (PaperLib.isPaper()) { + this.plugin.getSLF4JLogger().error(String.format(message, args), throwable); + } else { + this.plugin.getLogger().log(Level.SEVERE, String.format(message, args), throwable); + } + } + + @Override + public void error(@NotNull final String message, @NotNull final Object @NotNull... args) { + if (PaperLib.isPaper()) { + this.plugin.getSLF4JLogger().error(String.format(message, args)); + } else { + this.plugin.getLogger().severe(String.format(message, args)); + } + } + + @Override + public void warn(@NotNull final String message, @NotNull final Object @NotNull... args) { + if (PaperLib.isPaper()) { + this.plugin.getSLF4JLogger().warn(String.format(message, args)); + } else { + this.plugin.getLogger().warning(String.format(message, args)); + } + } + + @Override + public void info(@NotNull final String message, @NotNull final Object @NotNull... args) { + if (PaperLib.isPaper()) { + this.plugin.getSLF4JLogger().info(String.format(message, args)); + } else { + this.plugin.getLogger().info(String.format(message, args)); + } + } + + @Override + public void debug(@NotNull final String message, @NotNull final Object @NotNull... args) { + if (this.isDebugEnabled()) { + this.info(message, args); + } + } +} diff --git a/core/src/main/java/net/infumia/frame/metadata/CacheKeyExtractorEntityUniqueId.java b/core/src/main/java/net/infumia/frame/metadata/CacheKeyExtractorEntityUniqueId.java new file mode 100644 index 0000000..cbc5dbb --- /dev/null +++ b/core/src/main/java/net/infumia/frame/metadata/CacheKeyExtractorEntityUniqueId.java @@ -0,0 +1,19 @@ +package net.infumia.frame.metadata; + +import net.infumia.frame.util.Preconditions; +import org.bukkit.entity.Entity; +import org.bukkit.metadata.Metadatable; +import org.jetbrains.annotations.NotNull; + +public final class CacheKeyExtractorEntityUniqueId implements CacheKeyExtractor { + + @NotNull + @Override + public String apply(@NotNull final Metadatable metadatable) { + Preconditions.argument( + metadatable instanceof Entity, + "Only Entity type supported for metadata cache key extraction!" + ); + return ((Entity) metadatable).getUniqueId().toString(); + } +} diff --git a/core/src/main/java/net/infumia/frame/metadata/MetadataAccessFactoryImpl.java b/core/src/main/java/net/infumia/frame/metadata/MetadataAccessFactoryImpl.java new file mode 100644 index 0000000..62be55a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/metadata/MetadataAccessFactoryImpl.java @@ -0,0 +1,46 @@ +package net.infumia.frame.metadata; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.metadata.Metadatable; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public final class MetadataAccessFactoryImpl implements MetadataAccessFactory { + + private final Map cache = new ConcurrentHashMap<>(); + private final Plugin plugin; + private final CacheKeyExtractor cacheKeyExtractor; + + public MetadataAccessFactoryImpl( + @NotNull final Plugin plugin, + @NotNull final CacheKeyExtractor cacheKeyExtractor + ) { + this.plugin = plugin; + this.cacheKeyExtractor = cacheKeyExtractor; + } + + public MetadataAccessFactoryImpl(@NotNull final Plugin plugin) { + this(plugin, new CacheKeyExtractorEntityUniqueId()); + } + + @NotNull + @Override + public MetadataAccess getOrCreate(@NotNull final Metadatable metadatable) { + return this.cache.computeIfAbsent(this.cacheKeyExtractor.apply(metadatable), __ -> + new MetadataAccessImpl(this.plugin, metadatable) + ); + } + + @Override + public void clearCache(@NotNull final Collection metadatables) { + metadatables + .stream() + .map(this.cacheKeyExtractor) + .map(this.cache::remove) + .filter(Objects::nonNull) + .forEach(MetadataAccess::removeAll); + } +} diff --git a/core/src/main/java/net/infumia/frame/metadata/MetadataAccessImpl.java b/core/src/main/java/net/infumia/frame/metadata/MetadataAccessImpl.java new file mode 100644 index 0000000..de30b80 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/metadata/MetadataAccessImpl.java @@ -0,0 +1,96 @@ +package net.infumia.frame.metadata; + +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import net.infumia.frame.typedkey.TypedKey; +import net.infumia.frame.util.Preconditions; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.metadata.LazyMetadataValue; +import org.bukkit.metadata.Metadatable; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class MetadataAccessImpl implements MetadataAccess { + + private final Collection> registered = ConcurrentHashMap.newKeySet(); + private final Plugin plugin; + private final Metadatable metadatable; + + public MetadataAccessImpl( + @NotNull final Plugin plugin, + @NotNull final Metadatable metadatable + ) { + this.plugin = plugin; + this.metadatable = metadatable; + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T get(@NotNull final TypedKey key) { + return this.metadatable.getMetadata(key.key()) + .stream() + .filter(metadataValue -> Objects.equals(metadataValue.getOwningPlugin(), this.plugin)) + .findFirst() + .map(metadataValue -> (T) metadataValue.value()) + .orElse(null); + } + + @NotNull + @Override + public T getOrThrow(@NotNull final TypedKey key) { + return Preconditions.argumentNotNull( + this.get(key), + "Metadata value for key " + key + " not found!" + ); + } + + @Nullable + @Override + public T remove(@NotNull final TypedKey key) { + final T data = this.get(key); + this.registered.remove(key); + this.metadatable.removeMetadata(key.key(), this.plugin); + return data; + } + + @Override + public boolean has(@NotNull final TypedKey key) { + return this.metadatable.hasMetadata(key.key()); + } + + @Override + public void setFixed(@NotNull final TypedKey key, @NotNull final T value) { + this.registered.add(key); + this.metadatable.setMetadata(key.key(), new FixedMetadataValue(this.plugin, value)); + } + + @Override + public void setLazy( + @NotNull final TypedKey key, + @NotNull final Callable value, + @NotNull final LazyMetadataValue.CacheStrategy cacheStrategy + ) { + this.registered.add(key); + this.metadatable.setMetadata( + key.key(), + new LazyMetadataValue(this.plugin, cacheStrategy, value::call) + ); + } + + @Override + public void setLazy(@NotNull final TypedKey key, @NotNull final Callable value) { + this.setLazy(key, value, LazyMetadataValue.CacheStrategy.CACHE_AFTER_FIRST_EVAL); + } + + @Override + public void removeAll() { + for (final TypedKey key : this.registered) { + this.metadatable.removeMetadata(key.key(), this.plugin); + } + this.registered.clear(); + } +} diff --git a/core/src/main/java/net/infumia/frame/metadata/MetadataKeyHolder.java b/core/src/main/java/net/infumia/frame/metadata/MetadataKeyHolder.java new file mode 100644 index 0000000..eeba8e1 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/metadata/MetadataKeyHolder.java @@ -0,0 +1,32 @@ +package net.infumia.frame.metadata; + +import io.leangen.geantyref.TypeToken; +import java.util.Deque; +import java.util.Map; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.typedkey.TypedKey; +import net.infumia.frame.viewer.ContextualViewer; + +public interface MetadataKeyHolder { + TypedKey CONTEXTUAL_VIEWER = TypedKey.of( + ContextualViewer.class, + "contextual-viewer" + ); + + TypedKey TRANSITIONING_FROM = TypedKey.of( + ContextualViewer.class, + "transitioning-from" + ); + + TypedKey FORCED_CLOSE = TypedKey.of(boolean.class, "forced-close"); + + TypedKey> PREVIOUS_VIEWS = TypedKey.of( + new TypeToken>() {}, + "previous-views" + ); + + TypedKey> LAST_INTERACTION = TypedKey.of( + new TypeToken>() {}, + "last-interaction" + ); +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/PipelineConsumerImpl.java b/core/src/main/java/net/infumia/frame/pipeline/PipelineConsumerImpl.java new file mode 100644 index 0000000..4e4ba79 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/PipelineConsumerImpl.java @@ -0,0 +1,42 @@ +package net.infumia.frame.pipeline; + +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.service.ServicePipelineBuilder; +import net.infumia.frame.service.ServiceRepository; +import org.jetbrains.annotations.NotNull; + +public final class PipelineConsumerImpl implements PipelineConsumer { + + private final ServiceRepository repository; + + public PipelineConsumerImpl( + @NotNull final TypeToken> type, + @NotNull final PipelineServiceConsumer defaultService + ) { + this.repository = ServicePipelineBuilder.newBuilder().build().create(type, defaultService); + } + + @NotNull + @Override + public PipelineConsumer apply( + @NotNull final Implementation operation + ) { + this.repository.apply(operation); + return this; + } + + @NotNull + @Override + public CompletableFuture completeWith(@NotNull final B context) { + return this.repository.completeWith(context); + } + + @NotNull + @Override + public CompletableFuture completeWithAsync(@NotNull final B context) { + return this.repository.completeWithAsync(context); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/PipelineImpl.java b/core/src/main/java/net/infumia/frame/pipeline/PipelineImpl.java new file mode 100644 index 0000000..aef1198 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/PipelineImpl.java @@ -0,0 +1,39 @@ +package net.infumia.frame.pipeline; + +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.service.ServicePipelineBuilder; +import net.infumia.frame.service.ServiceRepository; +import org.jetbrains.annotations.NotNull; + +public class PipelineImpl implements Pipeline { + + private final ServiceRepository repository; + + public PipelineImpl( + @NotNull final TypeToken> type, + @NotNull final PipelineService defaultService + ) { + this.repository = ServicePipelineBuilder.newBuilder().build().create(type, defaultService); + } + + @NotNull + @Override + public Pipeline apply(@NotNull final Implementation operation) { + this.repository.apply(operation); + return this; + } + + @NotNull + @Override + public CompletableFuture completeWith(@NotNull final B context) { + return this.repository.completeWith(context); + } + + @NotNull + @Override + public CompletableFuture completeWithAsync(@NotNull final B context) { + return this.repository.completeWithAsync(context); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElements.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElements.java new file mode 100644 index 0000000..2c7aed2 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextElements.java @@ -0,0 +1,93 @@ +package net.infumia.frame.pipeline.context; + +import net.infumia.frame.context.element.ContextElementClear; +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.context.element.ContextElementRender; +import net.infumia.frame.context.element.ContextElementUpdate; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextElements { + final class Render implements PipelineContextElement.Render { + + private final ContextElementRender context; + + public Render(@NotNull final ContextElementRender context) { + this.context = context; + } + + @NotNull + @Override + public ContextElementRender context() { + return this.context; + } + } + + final class Clear implements PipelineContextElement.Clear { + + private final ContextElementClear context; + + public Clear(@NotNull final ContextElementClear context) { + this.context = context; + } + + @NotNull + @Override + public ContextElementClear context() { + return this.context; + } + } + + final class Update implements PipelineContextElement.Update { + + private final ContextElementUpdate context; + + private boolean cancelled; + + public Update(@NotNull final ContextElementUpdate context) { + this.context = context; + } + + @NotNull + @Override + public ContextElementUpdate context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } + + final class Click implements PipelineContextElement.Click { + + private final ContextElementClick context; + + private boolean cancelled; + + public Click(@NotNull final ContextElementClick context) { + this.context = context; + } + + @NotNull + @Override + public ContextElementClick context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManagers.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManagers.java new file mode 100644 index 0000000..5438c77 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextManagers.java @@ -0,0 +1,101 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import net.infumia.frame.Frame; +import net.infumia.frame.view.View; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextManagers { + final class ViewCreated implements PipelineContextManager.ViewCreated { + + private final Frame manager; + private final Collection> registeredViews; + + public ViewCreated( + @NotNull final Frame manager, + @NotNull final Collection> registeredViews + ) { + this.manager = manager; + this.registeredViews = registeredViews; + } + + @NotNull + @Override + public Collection> registeredViews() { + return this.registeredViews; + } + + @NotNull + @Override + public Frame frame() { + return this.manager; + } + } + + final class ViewRegistered implements PipelineContextManager.ViewRegistered { + + private final Frame manager; + private final Collection registeredViews; + + public ViewRegistered( + @NotNull final Frame manager, + @NotNull final Collection registeredViews + ) { + this.manager = manager; + this.registeredViews = registeredViews; + } + + @NotNull + @Override + public Frame frame() { + return this.manager; + } + + @NotNull + @Override + public Collection registeredViews() { + return this.registeredViews; + } + } + + final class ListenerRegistered implements PipelineContextManager.ListenerRegistered { + + private final Frame manager; + + public ListenerRegistered(@NotNull final Frame manager) { + this.manager = manager; + } + + @NotNull + @Override + public Frame frame() { + return this.manager; + } + } + + final class ViewUnregistered implements PipelineContextManager.ViewUnregistered { + + private final Frame manager; + private final Collection unregisteredViews; + + public ViewUnregistered( + @NotNull final Frame manager, + @NotNull final Collection unregisteredViews + ) { + this.manager = manager; + this.unregisteredViews = unregisteredViews; + } + + @NotNull + @Override + public Frame frame() { + return this.manager; + } + + @NotNull + @Override + public Collection unregisteredViews() { + return this.unregisteredViews; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRenders.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRenders.java new file mode 100644 index 0000000..c9d53df --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextRenders.java @@ -0,0 +1,164 @@ +package net.infumia.frame.pipeline.context; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextResume; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextRenders { + final class FirstRender implements PipelineContextRender.FirstRender { + + private final List elements = new ArrayList<>(); + private final ContextRender context; + + public FirstRender(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @NotNull + @Override + public List elements() { + return Collections.unmodifiableList(this.elements); + } + + @Override + public void addElement(@NotNull final Element element) { + this.elements.add(0, (ElementRich) element); + } + } + + final class OpenContainer implements PipelineContextRender.OpenContainer { + + private final ContextRender context; + private final Collection viewers; + + public OpenContainer( + @NotNull final ContextRender context, + @NotNull final Collection viewers + ) { + this.context = context; + this.viewers = viewers; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } + + final class Resume implements PipelineContextRender.Resume { + + private final ContextResume context; + + public Resume(@NotNull final ContextResume context) { + this.context = context; + } + + @NotNull + @Override + public ContextResume context() { + return this.context; + } + } + + final class CloseContainer implements PipelineContextRender.CloseContainer { + + private final ContextRender context; + private final Collection viewers; + + public CloseContainer( + @NotNull final ContextRender context, + @NotNull final Collection viewers + ) { + this.context = context; + this.viewers = viewers; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } + + final class StartUpdate implements PipelineContextRender.StartUpdate { + + private final ContextRender context; + private boolean cancelled; + + public StartUpdate(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } + + final class StopUpdate implements PipelineContextRender.StopUpdate { + + private final ContextRender context; + + public StopUpdate(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + } + + final class Update implements PipelineContextRender.Update { + + private final ContextRender context; + + public Update(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextStates.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextStates.java new file mode 100644 index 0000000..66a4099 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextStates.java @@ -0,0 +1,88 @@ +package net.infumia.frame.pipeline.context; + +import net.infumia.frame.Frame; +import net.infumia.frame.state.State; +import net.infumia.frame.state.value.StateValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface PipelineContextStates { + final class Access implements PipelineContextState.Access { + + public final Frame manager; + private final State state; + private final StateValue value; + + public Access( + @NotNull final Frame manager, + @NotNull final State state, + @NotNull final StateValue value + ) { + this.manager = manager; + this.state = state; + this.value = value; + } + + @NotNull + @Override + public Frame manager() { + return this.manager; + } + + @NotNull + @Override + public State state() { + return this.state; + } + + @NotNull + @Override + public StateValue value() { + return this.value; + } + } + + final class Update implements PipelineContextState.Update { + + public final Frame manager; + private final State state; + private final Object oldValue; + private final StateValue value; + + public Update( + @NotNull final Frame manager, + @NotNull final State state, + @Nullable final Object oldValue, + @NotNull final StateValue value + ) { + this.manager = manager; + this.state = state; + this.oldValue = oldValue; + this.value = value; + } + + @NotNull + @Override + public Frame manager() { + return this.manager; + } + + @NotNull + @Override + public State state() { + return this.state; + } + + @Nullable + @Override + public Object oldValue() { + return this.oldValue; + } + + @NotNull + @Override + public StateValue value() { + return this.value; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewers.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewers.java new file mode 100644 index 0000000..9d04c9f --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViewers.java @@ -0,0 +1,60 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextViewers { + final class Added implements PipelineContextViewer.Added { + + private final ContextRender context; + private final Collection viewers; + + public Added( + @NotNull final ContextRender context, + @NotNull final Collection viewers + ) { + this.context = context; + this.viewers = viewers; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } + + final class Removed implements PipelineContextViewer.Removed { + + private final ContextRender context; + private final Collection viewers; + + public Removed( + @NotNull final ContextRender context, + @NotNull final Collection viewers + ) { + this.context = context; + this.viewers = viewers; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViews.java b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViews.java new file mode 100644 index 0000000..5ff321a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/context/PipelineContextViews.java @@ -0,0 +1,372 @@ +package net.infumia.frame.pipeline.context; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.slot.LayoutSlotImpl; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public interface PipelineContextViews { + final class Init implements PipelineContextView.Init { + + private final View view; + + public Init(@NotNull final View view) { + this.view = view; + } + + @NotNull + @Override + public View view() { + return this.view; + } + } + + final class CreateViewers implements PipelineContextView.CreateViewers { + + private final View view; + private final Collection viewers; + + public CreateViewers(@NotNull final View view, @NotNull final Collection viewers) { + this.view = view; + this.viewers = viewers; + } + + @NotNull + @Override + public View view() { + return this.view; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } + + final class CreateContext implements PipelineContextView.CreateContext { + + private final View view; + private final Collection viewers; + private final TypedKeyStorageImmutable initialData; + + public CreateContext( + @NotNull final View view, + @NotNull final Collection viewers, + @NotNull final TypedKeyStorageImmutable initialData + ) { + this.view = view; + this.viewers = viewers; + this.initialData = initialData; + } + + @NotNull + @Override + public View view() { + return this.view; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + + @NotNull + @Override + public TypedKeyStorageImmutable initialData() { + return this.initialData; + } + } + + final class Transition implements PipelineContextView.Transition { + + private final ContextBase context; + private final Collection viewers; + + public Transition(@NotNull final ContextBase context, final Collection viewers) { + this.context = context; + this.viewers = viewers; + } + + @NotNull + @Override + public ContextBase context() { + return this.context; + } + + @NotNull + @Override + public Collection viewers() { + return this.viewers; + } + } + + final class Open implements PipelineContextView.Open { + + private final ContextOpen context; + private boolean cancelled; + + public Open(@NotNull final ContextOpen context) { + this.context = context; + } + + @NotNull + @Override + public ContextOpen context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } + + final class ProcessConfigModifier implements PipelineContextView.ProcessConfigModifier { + + private final ContextOpen context; + + public ProcessConfigModifier(@NotNull final ContextOpen context) { + this.context = context; + } + + @NotNull + @Override + public ContextOpen context() { + return this.context; + } + } + + final class CreateContainer implements PipelineContextView.CreateContainer { + + private final ContextBase context; + private final ViewConfig config; + + public CreateContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config + ) { + this.context = context; + this.config = config; + } + + @NotNull + @Override + public ContextBase context() { + return this.context; + } + + @NotNull + @Override + public ViewConfig config() { + return this.config; + } + } + + final class ModifyContainer implements PipelineContextView.ModifyContainer { + + private final ContextBase context; + private final ViewConfig config; + private ViewContainer container; + + public ModifyContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + this.context = context; + this.config = config; + this.container = container; + } + + @NotNull + @Override + public ContextBase context() { + return this.context; + } + + @NotNull + @Override + public ViewConfig config() { + return this.config; + } + + @NotNull + @Override + public ViewContainer container() { + return this.container; + } + + @Override + public void modifyContainer(@NotNull final ViewContainer newContainer) { + this.container = newContainer; + } + } + + final class LayoutResolution implements PipelineContextView.LayoutResolution { + + private final Map layouts = new ConcurrentHashMap<>(); + private final ContextBase context; + private final ViewConfig config; + private final ViewContainer container; + + public LayoutResolution( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + this.context = context; + this.config = config; + this.container = container; + } + + @NotNull + @Override + public ContextBase context() { + return this.context; + } + + @NotNull + @Override + public ViewConfig config() { + return this.config; + } + + @NotNull + @Override + public ViewContainer container() { + return this.container; + } + + @NotNull + @Override + public Map layouts() { + return Collections.unmodifiableMap(this.layouts); + } + + @Override + public void addLayout(final char character, @NotNull final Collection indexes) { + this.layouts.computeIfAbsent(character, __ -> + new LayoutSlotImpl( + character, + indexes.stream().mapToInt(value -> value).toArray() + ) + ); + } + } + + final class CreateRender implements PipelineContextView.CreateRender { + + private final ContextBase context; + private final ViewConfig config; + private final ViewContainer container; + private final Map layouts; + + public CreateRender( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container, + @NotNull final Map layouts + ) { + this.context = context; + this.config = config; + this.container = container; + this.layouts = layouts; + } + + @NotNull + @Override + public ContextBase context() { + return this.context; + } + + @NotNull + @Override + public ViewConfig config() { + return this.config; + } + + @NotNull + @Override + public ViewContainer container() { + return this.container; + } + + @NotNull + @Override + public Map layouts() { + return this.layouts; + } + } + + final class Click implements PipelineContextView.Click { + + private final ContextClick context; + private boolean cancelled; + + public Click(@NotNull final ContextClick context) { + this.context = context; + } + + @NotNull + @Override + public ContextClick context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } + + final class Close implements PipelineContextView.Close { + + private final ContextClose context; + private boolean cancelled; + + public Close(@NotNull final ContextClose context) { + this.context = context; + } + + @NotNull + @Override + public ContextClose context() { + return this.context; + } + + @Override + public boolean cancelled() { + return this.cancelled; + } + + @Override + public void cancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElementImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElementImpl.java new file mode 100644 index 0000000..d76f842 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorElementImpl.java @@ -0,0 +1,119 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.element.ContextElementClearImpl; +import net.infumia.frame.context.element.ContextElementClickImpl; +import net.infumia.frame.context.element.ContextElementRenderImpl; +import net.infumia.frame.context.element.ContextElementUpdateImpl; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.pipeline.context.PipelineContextElements; +import net.infumia.frame.pipeline.holder.PipelineHolderElement; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExecutorElementImpl implements PipelineExecutorElement { + + private final PipelineHolderElement pipelines = PipelineHolderElement.BASE.createNew(); + private final ElementRich element; + + public PipelineExecutorElementImpl(@NotNull final ElementRich element) { + this.element = element; + } + + @NotNull + @Override + public CompletableFuture executeRender( + @NotNull final ContextRender context + ) { + return this.pipelines.render() + .completeWith( + new PipelineContextElements.Render( + new ContextElementRenderImpl(context, this.element) + ) + ); + } + + @NotNull + @Override + public CompletableFuture executeUpdate( + @NotNull final ContextRender context, + final boolean forced + ) { + return this.pipelines.update() + .completeWith( + new PipelineContextElements.Update( + new ContextElementUpdateImpl(context, this.element, forced) + ) + ); + } + + @NotNull + @Override + public CompletableFuture executeClick( + @NotNull final ContextClick context + ) { + return this.pipelines.click() + .completeWith( + new PipelineContextElements.Click( + new ContextElementClickImpl(context, this.element) + ) + ); + } + + @NotNull + @Override + public CompletableFuture executeClear( + @NotNull final ContextRender context + ) { + return this.pipelines.clear() + .completeWith( + new PipelineContextElements.Clear( + new ContextElementClearImpl(context, this.element) + ) + ); + } + + @Override + public void applyRender( + @NotNull final Implementation< + PipelineContextElement.Render, + ConsumerService.State + > implementation + ) { + this.pipelines.render().apply(implementation); + } + + @Override + public void applyUpdate( + @NotNull final Implementation< + PipelineContextElement.Update, + ConsumerService.State + > implementation + ) { + this.pipelines.update().apply(implementation); + } + + @Override + public void applyClick( + @NotNull final Implementation< + PipelineContextElement.Click, + ConsumerService.State + > implementation + ) { + this.pipelines.click().apply(implementation); + } + + @Override + public void applyClear( + @NotNull final Implementation< + PipelineContextElement.Clear, + ConsumerService.State + > implementation + ) { + this.pipelines.clear().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManagerImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManagerImpl.java new file mode 100644 index 0000000..6d9fcf9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorManagerImpl.java @@ -0,0 +1,112 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.Frame; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import net.infumia.frame.pipeline.context.PipelineContextManagers; +import net.infumia.frame.pipeline.holder.PipelineHolderManager; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.view.View; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExecutorManagerImpl implements PipelineExecutorManager { + + private final PipelineHolderManager pipelines = PipelineHolderManager.BASE.createNew(); + private final Frame manager; + + public PipelineExecutorManagerImpl(@NotNull final Frame manager) { + this.manager = manager; + } + + @NotNull + @Override + public CompletableFuture> executeViewCreated( + @NotNull final Collection> registeredViews + ) { + return this.pipelines.viewCreated() + .completeWith( + new PipelineContextManagers.ViewCreated( + this.manager, + Collections.unmodifiableCollection(registeredViews) + ) + ); + } + + @NotNull + @Override + public CompletableFuture> executeViewRegistered( + @NotNull final Collection registeredViews + ) { + return this.pipelines.viewRegistered() + .completeWith( + new PipelineContextManagers.ViewRegistered( + this.manager, + Collections.unmodifiableCollection(registeredViews) + ) + ); + } + + @NotNull + @Override + public CompletableFuture executeListenersRegistered() { + return this.pipelines.listenersRegistered() + .completeWith(new PipelineContextManagers.ListenerRegistered(this.manager)); + } + + @NotNull + @Override + public CompletableFuture executeViewUnregistered( + @NotNull final Collection unregisteredViews + ) { + return this.pipelines.viewUnregistered() + .completeWith( + new PipelineContextManagers.ViewUnregistered( + this.manager, + Collections.unmodifiableCollection(unregisteredViews) + ) + ); + } + + @Override + public void applyViewCreated( + @NotNull final Implementation< + PipelineContextManager.ViewCreated, + Collection + > implementation + ) { + this.pipelines.viewCreated().apply(implementation); + } + + @Override + public void applyViewRegistered( + @NotNull final Implementation< + PipelineContextManager.ViewRegistered, + Collection + > implementation + ) { + this.pipelines.viewRegistered().apply(implementation); + } + + @Override + public void applyListenersRegistered( + @NotNull final Implementation< + PipelineContextManager.ListenerRegistered, + ConsumerService.State + > implementation + ) { + this.pipelines.listenersRegistered().apply(implementation); + } + + @Override + public void applyViewUnregistered( + @NotNull final Implementation< + PipelineContextManager.ViewUnregistered, + ConsumerService.State + > implementation + ) { + this.pipelines.viewUnregistered().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRenderImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRenderImpl.java new file mode 100644 index 0000000..6515d27 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorRenderImpl.java @@ -0,0 +1,155 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextResumeImpl; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.pipeline.context.PipelineContextRenders; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.pipeline.context.PipelineContextViews; +import net.infumia.frame.pipeline.holder.PipelineHolderRender; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExecutorRenderImpl implements PipelineExecutorRender { + + private final PipelineHolderRender pipelines = PipelineHolderRender.BASE.createNew(); + private final ContextRender context; + + public PipelineExecutorRenderImpl(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public CompletableFuture executeFirstRender() { + return this.pipelines.firstRender() + .completeWith(new PipelineContextRenders.FirstRender(this.context)); + } + + @NotNull + @Override + public CompletableFuture executeTransition( + @NotNull final Collection viewers + ) { + return this.pipelines.transition() + .completeWith(new PipelineContextViews.Transition(this.context, viewers)); + } + + @NotNull + @Override + public CompletableFuture executeOpenContainer( + @NotNull final Collection viewers + ) { + return this.pipelines.openContainer() + .completeWith(new PipelineContextRenders.OpenContainer(this.context, viewers)); + } + + @NotNull + @Override + public CompletableFuture executeStartUpdate() { + return this.pipelines.startUpdate() + .completeWith(new PipelineContextRenders.StartUpdate(this.context)); + } + + @NotNull + @Override + public CompletableFuture executeResume( + @NotNull final ContextRender from, + @NotNull final Collection viewers + ) { + return this.pipelines.resume() + .completeWith( + new PipelineContextRenders.Resume( + new ContextResumeImpl(this.context, from, viewers) + ) + ); + } + + @NotNull + @Override + public CompletableFuture executeStopUpdate() { + return this.pipelines.stopUpdate() + .completeWith(new PipelineContextRenders.StopUpdate(this.context)); + } + + @NotNull + @Override + public CompletableFuture executeUpdate() { + return this.pipelines.update() + .completeWith(new PipelineContextRenders.Update(this.context)); + } + + @Override + public void applyFirstRender( + @NotNull final Implementation< + PipelineContextRender.FirstRender, + ConsumerService.State + > implementation + ) { + this.pipelines.firstRender().apply(implementation); + } + + @Override + public void applyTransition( + @NotNull final Implementation< + PipelineContextView.Transition, + ConsumerService.State + > implementation + ) { + this.pipelines.transition().apply(implementation); + } + + @Override + public void applyOpenContainer( + @NotNull final Implementation< + PipelineContextRender.OpenContainer, + ConsumerService.State + > implementation + ) { + this.pipelines.openContainer().apply(implementation); + } + + @Override + public void applyStartUpdate( + @NotNull final Implementation< + PipelineContextRender.StartUpdate, + ConsumerService.State + > implementation + ) { + this.pipelines.startUpdate().apply(implementation); + } + + @Override + public void applyResume( + @NotNull final Implementation< + PipelineContextRender.Resume, + ConsumerService.State + > implementation + ) { + this.pipelines.resume().apply(implementation); + } + + @Override + public void applyStopUpdate( + @NotNull final Implementation< + PipelineContextRender.StopUpdate, + ConsumerService.State + > implementation + ) { + this.pipelines.stopUpdate().apply(implementation); + } + + @Override + public void applyUpdate( + @NotNull final Implementation< + PipelineContextRender.Update, + ConsumerService.State + > implementation + ) { + this.pipelines.update().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorStateImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorStateImpl.java new file mode 100644 index 0000000..09e3ab9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorStateImpl.java @@ -0,0 +1,66 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.pipeline.context.PipelineContextState; +import net.infumia.frame.pipeline.context.PipelineContextStates; +import net.infumia.frame.pipeline.holder.PipelineHolderState; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.state.State; +import net.infumia.frame.state.value.StateValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class PipelineExecutorStateImpl implements PipelineExecutorState { + + private final PipelineHolderState pipelines = PipelineHolderState.BASE.createNew(); + private final ContextBase context; + + public PipelineExecutorStateImpl(@NotNull final ContextBase context) { + this.context = context; + } + + @NotNull + @Override + public CompletableFuture executeAccess( + @NotNull final State state, + @NotNull final StateValue value + ) { + return this.pipelines.access() + .completeWith(new PipelineContextStates.Access(this.context.manager(), state, value)); + } + + @NotNull + @Override + public CompletableFuture executeUpdate( + @NotNull final State state, + @Nullable final Object oldValue, + @NotNull final StateValue value + ) { + return this.pipelines.update() + .completeWith( + new PipelineContextStates.Update(this.context.manager(), state, oldValue, value) + ); + } + + @Override + public void applyAccess( + @NotNull final Implementation< + PipelineContextState.Access, + ConsumerService.State + > implementation + ) { + this.pipelines.access().apply(implementation); + } + + @Override + public void applyUpdate( + @NotNull final Implementation< + PipelineContextState.Update, + ConsumerService.State + > implementation + ) { + this.pipelines.update().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewImpl.java new file mode 100644 index 0000000..6f4c36d --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewImpl.java @@ -0,0 +1,271 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextClickImpl; +import net.infumia.frame.context.view.ContextCloseImpl; +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.context.view.ContextOpenImpl; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.pipeline.context.PipelineContextViews; +import net.infumia.frame.pipeline.holder.PipelineHolderView; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.util.Pair; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.viewer.ContextualViewer; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExecutorViewImpl implements PipelineExecutorView { + + private final PipelineHolderView pipelines = PipelineHolderView.BASE.createNew(); + private final View view; + + public PipelineExecutorViewImpl(@NotNull final View view) { + this.view = view; + } + + @NotNull + @Override + public CompletableFuture executeInit( + @NotNull final ContextInit context + ) { + return this.pipelines.init().completeWith(new PipelineContextViews.Init(this.view)); + } + + @NotNull + @Override + public CompletableFuture> executeCreateViewers( + @NotNull final Collection viewers + ) { + return this.pipelines.createViewers() + .completeWith(new PipelineContextViews.CreateViewers(this.view, viewers)); + } + + @NotNull + @Override + public CompletableFuture executeCreateContext( + @NotNull final Collection viewers, + @NotNull final TypedKeyStorageImmutable initialData + ) { + return this.pipelines.createContext() + .completeWith(new PipelineContextViews.CreateContext(this.view, viewers, initialData)); + } + + @NotNull + @Override + public CompletableFuture> executeOpen( + @NotNull final ContextBase context + ) { + final ContextOpenImpl open = new ContextOpenImpl(context); + return this.pipelines.open() + .completeWith(new PipelineContextViews.Open(open)) + .thenApply(state -> Pair.of(state, open)); + } + + @NotNull + @Override + public CompletableFuture executeProcessConfigModifiers( + @NotNull final ContextOpen context + ) { + return this.pipelines.processConfigModifiers() + .completeWith(new PipelineContextViews.ProcessConfigModifier(context)); + } + + @NotNull + @Override + public CompletableFuture executeCreateContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config + ) { + return this.pipelines.createContainer() + .completeWith(new PipelineContextViews.CreateContainer(context, config)); + } + + @NotNull + @Override + public CompletableFuture< + Pair + > executeModifyContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + final PipelineContextViews.ModifyContainer ctx = new PipelineContextViews.ModifyContainer( + context, + config, + container + ); + return this.pipelines.modifyContainer() + .completeWith(ctx) + .thenApply(state -> Pair.of(state, ctx)); + } + + @NotNull + @Override + public CompletableFuture< + Pair> + > executeLayoutResolution( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + final PipelineContextView.LayoutResolution layoutResolution = + new PipelineContextViews.LayoutResolution(context, config, container); + return this.pipelines.layoutResolution() + .completeWith(layoutResolution) + .thenApply(state -> Pair.of(state, layoutResolution.layouts())); + } + + @NotNull + @Override + public CompletableFuture executeCreateRender( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container, + @NotNull final Map layouts + ) { + return this.pipelines.createRender() + .completeWith( + new PipelineContextViews.CreateRender(context, config, container, layouts) + ); + } + + @NotNull + @Override + public CompletableFuture executeClick( + @NotNull final ContextualViewer clicker, + @NotNull final InventoryClickEvent event + ) { + return this.pipelines.click() + .completeWith(new PipelineContextViews.Click(new ContextClickImpl(clicker, event))); + } + + @NotNull + @Override + public CompletableFuture executeClose( + @NotNull final ContextualViewer viewer, + final boolean forced + ) { + return this.pipelines.close() + .completeWith(new PipelineContextViews.Close(new ContextCloseImpl(viewer, forced))); + } + + @Override + public void applyInit( + @NotNull final Implementation< + PipelineContextView.Init, + ConsumerService.State + > implementation + ) { + this.pipelines.init().apply(implementation); + } + + @Override + public void applyCreateViewers( + @NotNull final Implementation< + PipelineContextView.CreateViewers, + Collection + > implementation + ) { + this.pipelines.createViewers().apply(implementation); + } + + @Override + public void applyCreateContext( + @NotNull final Implementation implementation + ) { + this.pipelines.createContext().apply(implementation); + } + + @Override + public void applyOpen( + @NotNull final Implementation< + PipelineContextView.Open, + ConsumerService.State + > implementation + ) { + this.pipelines.open().apply(implementation); + } + + @Override + public void applyProcessConfigModifiers( + @NotNull final Implementation< + PipelineContextView.ProcessConfigModifier, + ConsumerService.State + > implementation + ) { + this.pipelines.processConfigModifiers().apply(implementation); + } + + @Override + public void applyCreateContainer( + @NotNull final Implementation< + PipelineContextView.CreateContainer, + ViewContainer + > implementation + ) { + this.pipelines.createContainer().apply(implementation); + } + + @Override + public void applyModifyContainer( + @NotNull final Implementation< + PipelineContextView.ModifyContainer, + ConsumerService.State + > implementation + ) { + this.pipelines.modifyContainer().apply(implementation); + } + + @Override + public void applyLayoutResolution( + @NotNull final Implementation< + PipelineContextView.LayoutResolution, + ConsumerService.State + > implementation + ) { + this.pipelines.layoutResolution().apply(implementation); + } + + @Override + public void applyCreateRender( + @NotNull final Implementation< + PipelineContextView.CreateRender, + ContextRender + > implementation + ) { + this.pipelines.createRender().apply(implementation); + } + + @Override + public void applyClick( + @NotNull final Implementation< + PipelineContextView.Click, + ConsumerService.State + > implementation + ) { + this.pipelines.click().apply(implementation); + } + + @Override + public void applyClose( + @NotNull final Implementation< + PipelineContextView.Close, + ConsumerService.State + > implementation + ) { + this.pipelines.close().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewerImpl.java b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewerImpl.java new file mode 100644 index 0000000..15df822 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/executor/PipelineExecutorViewerImpl.java @@ -0,0 +1,60 @@ +package net.infumia.frame.pipeline.executor; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.pipeline.context.PipelineContextViewers; +import net.infumia.frame.pipeline.holder.PipelineHolderViewer; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class PipelineExecutorViewerImpl implements PipelineExecutorViewer { + + private final PipelineHolderViewer pipelines = PipelineHolderViewer.BASE.createNew(); + private final ContextRender context; + + public PipelineExecutorViewerImpl(@NotNull final ContextRender context) { + this.context = context; + } + + @NotNull + @Override + public CompletableFuture executeAdded( + @NotNull final Collection viewers + ) { + return this.pipelines.added() + .completeWith(new PipelineContextViewers.Added(this.context, viewers)); + } + + @NotNull + @Override + public CompletableFuture executeRemoved( + @NotNull final Collection viewers + ) { + return this.pipelines.removed() + .completeWith(new PipelineContextViewers.Removed(this.context, viewers)); + } + + @Override + public void applyAdded( + @NotNull final Implementation< + PipelineContextViewer.Added, + ConsumerService.State + > implementation + ) { + this.pipelines.added().apply(implementation); + } + + @Override + public void applyRemoved( + @NotNull final Implementation< + PipelineContextViewer.Removed, + ConsumerService.State + > implementation + ) { + this.pipelines.removed().apply(implementation); + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderElement.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderElement.java new file mode 100644 index 0000000..b35f7bd --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderElement.java @@ -0,0 +1,87 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import net.infumia.frame.pipeline.service.element.ServiceClear; +import net.infumia.frame.pipeline.service.element.ServiceClearLogging; +import net.infumia.frame.pipeline.service.element.ServiceClick; +import net.infumia.frame.pipeline.service.element.ServiceClickCancelOnClick; +import net.infumia.frame.pipeline.service.element.ServiceClickCloseOnClick; +import net.infumia.frame.pipeline.service.element.ServiceClickLogging; +import net.infumia.frame.pipeline.service.element.ServiceClickUpdateOnClick; +import net.infumia.frame.pipeline.service.element.ServiceRender; +import net.infumia.frame.pipeline.service.element.ServiceRenderLogging; +import net.infumia.frame.pipeline.service.element.ServiceUpdate; +import net.infumia.frame.pipeline.service.element.ServiceUpdateLogging; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderElement { + + private final PipelineConsumer render; + private final PipelineConsumer clear; + private final PipelineConsumer click; + private final PipelineConsumer update; + + public static final PipelineHolderElement BASE = new PipelineHolderElement( + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceRenderLogging.INSTANCE + ).register(ServiceRender.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceClearLogging.INSTANCE + ).register(ServiceClear.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceClickLogging.INSTANCE + ) + .register(ServiceClickCloseOnClick.INSTANCE) + .register(ServiceClickUpdateOnClick.INSTANCE) + .register(ServiceClick.INSTANCE) + .register(ServiceClickCancelOnClick.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceUpdateLogging.INSTANCE + ).register(ServiceUpdate.INSTANCE) + ); + + @NotNull + public PipelineConsumer render() { + return this.render; + } + + @NotNull + public PipelineConsumer clear() { + return this.clear; + } + + @NotNull + public PipelineConsumer click() { + return this.click; + } + + @NotNull + public PipelineConsumer update() { + return this.update; + } + + @NotNull + public PipelineHolderElement createNew() { + return new PipelineHolderElement(this.render, this.clear, this.click, this.update); + } + + public PipelineHolderElement( + @NotNull final PipelineConsumer render, + @NotNull final PipelineConsumer clear, + @NotNull final PipelineConsumer click, + @NotNull final PipelineConsumer update + ) { + this.render = render; + this.clear = clear; + this.click = click; + this.update = update; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderManager.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderManager.java new file mode 100644 index 0000000..fb98e47 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderManager.java @@ -0,0 +1,96 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import java.util.Collection; +import net.infumia.frame.pipeline.Pipeline; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineImpl; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import net.infumia.frame.pipeline.service.manager.ServiceListenerRegistered; +import net.infumia.frame.pipeline.service.manager.ServiceListenerRegisteredLogging; +import net.infumia.frame.pipeline.service.manager.ServiceViewCreated; +import net.infumia.frame.pipeline.service.manager.ServiceViewRegistered; +import net.infumia.frame.pipeline.service.manager.ServiceViewUnregisteredLogging; +import net.infumia.frame.view.View; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderManager { + + private final Pipeline> viewCreated; + private final Pipeline> viewRegistered; + private final PipelineConsumer listenersRegistered; + private final PipelineConsumer viewUnregistered; + + public static final PipelineHolderManager BASE = new PipelineHolderManager( + new PipelineImpl<>( + new TypeToken< + PipelineService> + >() {}, + ServiceViewCreated.INSTANCE + ), + new PipelineImpl<>( + new TypeToken< + PipelineService> + >() {}, + ServiceViewRegistered.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceListenerRegisteredLogging.INSTANCE + ).register(ServiceListenerRegistered.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceViewUnregisteredLogging.INSTANCE + ) + ); + + @NotNull + public Pipeline> viewCreated() { + return this.viewCreated; + } + + @NotNull + public Pipeline> viewRegistered() { + return this.viewRegistered; + } + + @NotNull + public PipelineConsumer listenersRegistered() { + return this.listenersRegistered; + } + + @NotNull + public PipelineConsumer viewUnregistered() { + return this.viewUnregistered; + } + + @NotNull + public PipelineHolderManager createNew() { + return new PipelineHolderManager( + this.viewCreated, + this.viewRegistered, + this.listenersRegistered, + this.viewUnregistered + ); + } + + public PipelineHolderManager( + @NotNull final Pipeline> viewCreated, + @NotNull final Pipeline< + PipelineContextManager.ViewRegistered, + Collection + > viewRegistered, + @NotNull final PipelineConsumer< + PipelineContextManager.ListenerRegistered + > listenersRegistered, + @NotNull final PipelineConsumer viewUnregistered + ) { + this.viewCreated = viewCreated; + this.viewRegistered = viewRegistered; + this.listenersRegistered = listenersRegistered; + this.viewUnregistered = viewUnregistered; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderRender.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderRender.java new file mode 100644 index 0000000..b2835e9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderRender.java @@ -0,0 +1,151 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.pipeline.service.render.ServiceFirstRender; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderAvailableSlotResolution; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderConsumeNonRenderedElement; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderInitializeState; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderLayout; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderLogging; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderOnFirstRender; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderPagination; +import net.infumia.frame.pipeline.service.render.ServiceFirstRenderWatchState; +import net.infumia.frame.pipeline.service.render.ServiceOpenContainer; +import net.infumia.frame.pipeline.service.render.ServiceOpenContainerLogging; +import net.infumia.frame.pipeline.service.render.ServiceResumeLogging; +import net.infumia.frame.pipeline.service.render.ServiceResumeOnResume; +import net.infumia.frame.pipeline.service.render.ServiceStartUpdate; +import net.infumia.frame.pipeline.service.render.ServiceStartUpdateCancel; +import net.infumia.frame.pipeline.service.render.ServiceStartUpdateInvalidate; +import net.infumia.frame.pipeline.service.render.ServiceStartUpdateLogging; +import net.infumia.frame.pipeline.service.render.ServiceStopUpdate; +import net.infumia.frame.pipeline.service.render.ServiceStopUpdateLogging; +import net.infumia.frame.pipeline.service.render.ServiceUpdateLogging; +import net.infumia.frame.pipeline.service.render.ServiceUpdateOnUpdate; +import net.infumia.frame.pipeline.service.view.ServiceTransition; +import net.infumia.frame.pipeline.service.view.ServiceTransitionLogging; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderRender { + + private final PipelineConsumer firstRender; + private final PipelineConsumer transition; + private final PipelineConsumer openContainer; + private final PipelineConsumer resume; + private final PipelineConsumer startUpdate; + private final PipelineConsumer stopUpdate; + private final PipelineConsumer update; + + public static final PipelineHolderRender BASE = new PipelineHolderRender( + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceFirstRenderLogging.INSTANCE + ) + .register(ServiceFirstRender.INSTANCE) + .register(ServiceFirstRenderWatchState.INSTANCE) + .register(ServiceFirstRenderConsumeNonRenderedElement.INSTANCE) + .register(ServiceFirstRenderAvailableSlotResolution.INSTANCE) + .register(ServiceFirstRenderLayout.INSTANCE) + .register(ServiceFirstRenderOnFirstRender.INSTANCE) + .register(ServiceFirstRenderPagination.INSTANCE) + .register(ServiceFirstRenderInitializeState.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceTransitionLogging.INSTANCE + ).register(ServiceTransition.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceOpenContainerLogging.INSTANCE + ).register(ServiceOpenContainer.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceResumeLogging.INSTANCE + ).register(ServiceResumeOnResume.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceStartUpdateLogging.INSTANCE + ) + .register(ServiceStartUpdate.INSTANCE) + .register(ServiceStartUpdateCancel.INSTANCE) + .register(ServiceStartUpdateInvalidate.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceStopUpdateLogging.INSTANCE + ).register(ServiceStopUpdate.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceUpdateLogging.INSTANCE + ).register(ServiceUpdateOnUpdate.INSTANCE) + ); + + @NotNull + public PipelineConsumer firstRender() { + return this.firstRender; + } + + @NotNull + public PipelineConsumer transition() { + return this.transition; + } + + @NotNull + public PipelineConsumer openContainer() { + return this.openContainer; + } + + @NotNull + public PipelineConsumer resume() { + return this.resume; + } + + @NotNull + public PipelineConsumer startUpdate() { + return this.startUpdate; + } + + @NotNull + public PipelineConsumer stopUpdate() { + return this.stopUpdate; + } + + @NotNull + public PipelineConsumer update() { + return this.update; + } + + @NotNull + public PipelineHolderRender createNew() { + return new PipelineHolderRender( + this.firstRender, + this.transition, + this.openContainer, + this.resume, + this.startUpdate, + this.stopUpdate, + this.update + ); + } + + private PipelineHolderRender( + @NotNull final PipelineConsumer firstRender, + @NotNull final PipelineConsumer transition, + @NotNull final PipelineConsumer openContainer, + @NotNull final PipelineConsumer resume, + @NotNull final PipelineConsumer startUpdate, + @NotNull final PipelineConsumer stopUpdate, + @NotNull final PipelineConsumer update + ) { + this.firstRender = firstRender; + this.transition = transition; + this.openContainer = openContainer; + this.resume = resume; + this.startUpdate = startUpdate; + this.stopUpdate = stopUpdate; + this.update = update; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderState.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderState.java new file mode 100644 index 0000000..f7321bc --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderState.java @@ -0,0 +1,50 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextState; +import net.infumia.frame.pipeline.service.state.ServiceAccessLogging; +import net.infumia.frame.pipeline.service.state.ServiceUpdateLogging; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderState { + + private final PipelineConsumer access; + private final PipelineConsumer update; + + public static final PipelineHolderState BASE = new PipelineHolderState( + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceAccessLogging.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceUpdateLogging.INSTANCE + ) + ); + + @NotNull + public PipelineConsumer access() { + return this.access; + } + + @NotNull + public PipelineConsumer update() { + return this.update; + } + + @NotNull + public PipelineHolderState createNew() { + return new PipelineHolderState(this.access, this.update); + } + + public PipelineHolderState( + @NotNull final PipelineConsumer access, + @NotNull final PipelineConsumer update + ) { + this.access = access; + this.update = update; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderView.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderView.java new file mode 100644 index 0000000..fd9b1e5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderView.java @@ -0,0 +1,227 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import java.util.Collection; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.Pipeline; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineImpl; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.pipeline.service.view.ServiceClickCancel; +import net.infumia.frame.pipeline.service.view.ServiceClickElement; +import net.infumia.frame.pipeline.service.view.ServiceClickInteractionDelay; +import net.infumia.frame.pipeline.service.view.ServiceClickLogging; +import net.infumia.frame.pipeline.service.view.ServiceClickOnClick; +import net.infumia.frame.pipeline.service.view.ServiceClose; +import net.infumia.frame.pipeline.service.view.ServiceCloseCancel; +import net.infumia.frame.pipeline.service.view.ServiceCloseLogging; +import net.infumia.frame.pipeline.service.view.ServiceCloseOnClose; +import net.infumia.frame.pipeline.service.view.ServiceCreateContainer; +import net.infumia.frame.pipeline.service.view.ServiceCreateContext; +import net.infumia.frame.pipeline.service.view.ServiceCreateRender; +import net.infumia.frame.pipeline.service.view.ServiceCreateViewers; +import net.infumia.frame.pipeline.service.view.ServiceInitLogging; +import net.infumia.frame.pipeline.service.view.ServiceInitOnInit; +import net.infumia.frame.pipeline.service.view.ServiceInitWaitUntil; +import net.infumia.frame.pipeline.service.view.ServiceLayoutResolution; +import net.infumia.frame.pipeline.service.view.ServiceLayoutResolutionLogging; +import net.infumia.frame.pipeline.service.view.ServiceModifyContainerLogging; +import net.infumia.frame.pipeline.service.view.ServiceOpenInitializeState; +import net.infumia.frame.pipeline.service.view.ServiceOpenLogging; +import net.infumia.frame.pipeline.service.view.ServiceOpenOnOpen; +import net.infumia.frame.pipeline.service.view.ServiceOpenPreviousView; +import net.infumia.frame.pipeline.service.view.ServiceOpenWaitUntil; +import net.infumia.frame.pipeline.service.view.ServiceProcessConfigModifier; +import net.infumia.frame.pipeline.service.view.ServiceProcessConfigModifierAddSizeModifier; +import net.infumia.frame.pipeline.service.view.ServiceProcessConfigModifierLogging; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderView { + + private final PipelineConsumer init; + private final Pipeline> createViewers; + private final Pipeline createContext; + private final PipelineConsumer open; + private final PipelineConsumer< + PipelineContextView.ProcessConfigModifier + > processConfigModifiers; + private final Pipeline createContainer; + private final PipelineConsumer modifyContainer; + private final PipelineConsumer layoutResolution; + private final Pipeline createRender; + private final PipelineConsumer click; + private final PipelineConsumer close; + + public static final PipelineHolderView BASE = new PipelineHolderView( + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceInitLogging.INSTANCE + ) + .register(ServiceInitWaitUntil.INSTANCE) + .register(ServiceInitOnInit.INSTANCE), + new PipelineImpl<>( + new TypeToken< + PipelineService> + >() {}, + ServiceCreateViewers.INSTANCE + ), + new PipelineImpl<>( + new TypeToken>() {}, + ServiceCreateContext.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceOpenLogging.INSTANCE + ) + .register(ServiceOpenWaitUntil.INSTANCE) + .register(ServiceOpenOnOpen.INSTANCE) + .register(ServiceOpenInitializeState.INSTANCE) + .register(ServiceOpenPreviousView.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceProcessConfigModifierLogging.INSTANCE + ) + .register(ServiceProcessConfigModifier.INSTANCE) + .register(ServiceProcessConfigModifierAddSizeModifier.INSTANCE), + new PipelineImpl<>( + new TypeToken>() {}, + ServiceCreateContainer.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceModifyContainerLogging.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceLayoutResolutionLogging.INSTANCE + ).register(ServiceLayoutResolution.INSTANCE), + new PipelineImpl<>( + new TypeToken>() {}, + ServiceCreateRender.INSTANCE + ), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceClickLogging.INSTANCE + ) + .register(ServiceClickElement.INSTANCE) + .register(ServiceClickOnClick.INSTANCE) + .register(ServiceClickInteractionDelay.INSTANCE) + .register(ServiceClickCancel.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceCloseLogging.INSTANCE + ) + .register(ServiceClose.INSTANCE) + .register(ServiceCloseCancel.INSTANCE) + .register(ServiceCloseOnClose.INSTANCE) + ); + + @NotNull + public PipelineConsumer init() { + return this.init; + } + + @NotNull + public Pipeline> createViewers() { + return this.createViewers; + } + + @NotNull + public Pipeline createContext() { + return this.createContext; + } + + @NotNull + public PipelineConsumer open() { + return this.open; + } + + @NotNull + public PipelineConsumer processConfigModifiers() { + return this.processConfigModifiers; + } + + @NotNull + public Pipeline createContainer() { + return this.createContainer; + } + + @NotNull + public PipelineConsumer modifyContainer() { + return this.modifyContainer; + } + + @NotNull + public PipelineConsumer layoutResolution() { + return this.layoutResolution; + } + + @NotNull + public Pipeline createRender() { + return this.createRender; + } + + @NotNull + public PipelineConsumer click() { + return this.click; + } + + @NotNull + public PipelineConsumer close() { + return this.close; + } + + @NotNull + public PipelineHolderView createNew() { + return new PipelineHolderView( + this.init, + this.createViewers, + this.createContext, + this.open, + this.processConfigModifiers, + this.createContainer, + this.modifyContainer, + this.layoutResolution, + this.createRender, + this.click, + this.close + ); + } + + public PipelineHolderView( + @NotNull final PipelineConsumer init, + @NotNull final Pipeline< + PipelineContextView.CreateViewers, + Collection + > createViewers, + @NotNull final Pipeline createContext, + @NotNull final PipelineConsumer open, + @NotNull final PipelineConsumer< + PipelineContextView.ProcessConfigModifier + > processConfigModifiers, + @NotNull final Pipeline createContainer, + @NotNull final PipelineConsumer modifyContainer, + @NotNull final PipelineConsumer layoutResolution, + @NotNull final Pipeline createRender, + @NotNull final PipelineConsumer click, + @NotNull final PipelineConsumer close + ) { + this.init = init; + this.createViewers = createViewers; + this.createContext = createContext; + this.open = open; + this.processConfigModifiers = processConfigModifiers; + this.createContainer = createContainer; + this.modifyContainer = modifyContainer; + this.layoutResolution = layoutResolution; + this.createRender = createRender; + this.click = click; + this.close = close; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderViewer.java b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderViewer.java new file mode 100644 index 0000000..175e3f8 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/holder/PipelineHolderViewer.java @@ -0,0 +1,64 @@ +package net.infumia.frame.pipeline.holder; + +import io.leangen.geantyref.TypeToken; +import net.infumia.frame.pipeline.PipelineConsumer; +import net.infumia.frame.pipeline.PipelineConsumerImpl; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.pipeline.service.viewer.ServiceAdded; +import net.infumia.frame.pipeline.service.viewer.ServiceAddedContextualViewer; +import net.infumia.frame.pipeline.service.viewer.ServiceAddedLogging; +import net.infumia.frame.pipeline.service.viewer.ServiceAddedOnViewerAdded; +import net.infumia.frame.pipeline.service.viewer.ServiceRemoved; +import net.infumia.frame.pipeline.service.viewer.ServiceRemovedContextualViewer; +import net.infumia.frame.pipeline.service.viewer.ServiceRemovedLogging; +import net.infumia.frame.pipeline.service.viewer.ServiceRemovedOnViewerRemoved; +import net.infumia.frame.pipeline.service.viewer.ServiceRemovedStopUpdateTask; +import org.jetbrains.annotations.NotNull; + +public final class PipelineHolderViewer { + + private final PipelineConsumer added; + private final PipelineConsumer removed; + + public static final PipelineHolderViewer BASE = new PipelineHolderViewer( + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceAddedLogging.INSTANCE + ) + .register(ServiceAddedOnViewerAdded.INSTANCE) + .register(ServiceAdded.INSTANCE) + .register(ServiceAddedContextualViewer.INSTANCE), + new PipelineConsumerImpl<>( + new TypeToken>() {}, + ServiceRemovedLogging.INSTANCE + ) + .register(ServiceRemovedStopUpdateTask.INSTANCE) + .register(ServiceRemovedContextualViewer.INSTANCE) + .register(ServiceRemoved.INSTANCE) + .register(ServiceRemovedOnViewerRemoved.INSTANCE) + ); + + @NotNull + public PipelineConsumer added() { + return this.added; + } + + @NotNull + public PipelineConsumer removed() { + return this.removed; + } + + @NotNull + public PipelineHolderViewer createNew() { + return new PipelineHolderViewer(this.added, this.removed); + } + + public PipelineHolderViewer( + @NotNull final PipelineConsumer added, + @NotNull final PipelineConsumer removed + ) { + this.added = added; + this.removed = removed; + } +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClear.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClear.java new file mode 100644 index 0000000..474db64 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClear.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandlerHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClear implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClear(); + + public static final String KEY = "clear"; + + @Override + public String key() { + return ServiceClear.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextElement.Clear ctx) { + final Element element = ctx.context().element(); + if (element instanceof ElementEventHandlerHolder) { + return ((ElementEventHandlerHolder) element).eventHandler().handleClear(ctx); + } else { + return CompletableFuture.completedFuture(State.CONTINUE); + } + } + + private ServiceClear() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClearLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClearLogging.java new file mode 100644 index 0000000..780f763 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClearLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementClear; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClearLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClearLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceClearLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Clear ctx) { + final ContextElementClear context = ctx.context(); + final String key = ((ElementRich) context.element()).key(); + // TODO: portlek, Add more detailed message. + context.manager().logger().debug("Element '%s' is cleared.", key == null ? "null" : key); + } + + private ServiceClearLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClick.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClick.java new file mode 100644 index 0000000..4baa202 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClick.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandlerHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClick implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClick(); + + public static final String KEY = "click"; + + @Override + public String key() { + return ServiceClick.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextElement.Click ctx) { + final Element element = ctx.context().element(); + if (element instanceof ElementEventHandlerHolder) { + return ((ElementEventHandlerHolder) element).eventHandler().handleClick(ctx); + } else { + return CompletableFuture.completedFuture(State.CONTINUE); + } + } + + private ServiceClick() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCancelOnClick.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCancelOnClick.java new file mode 100644 index 0000000..dc4a0c9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCancelOnClick.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickCancelOnClick + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickCancelOnClick(); + + public static final String KEY = "cancel-on-click"; + + @Override + public String key() { + return ServiceClickCancelOnClick.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Click ctx) { + final ContextElementClick context = ctx.context(); + if (context.element().cancelOnClick()) { + context.cancelled(true); + } + } + + private ServiceClickCancelOnClick() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCloseOnClick.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCloseOnClick.java new file mode 100644 index 0000000..63c25ff --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickCloseOnClick.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickCloseOnClick + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickCloseOnClick(); + + public static final String KEY = "close-on-click"; + + @Override + public String key() { + return ServiceClickCloseOnClick.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Click ctx) { + final ContextElementClick context = ctx.context(); + if (context.element().closeOnClick()) { + context.closeForViewer(); + } + } + + private ServiceClickCloseOnClick() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickLogging.java new file mode 100644 index 0000000..b52bf5f --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceClickLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Click ctx) { + final ContextElementClick context = ctx.context(); + final String key = ((ElementRich) context.element()).key(); + // TODO: portlek, Add more detailed message. + context.manager().logger().debug("Element '%s' is clicked.", key == null ? "null" : key); + } + + private ServiceClickLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickUpdateOnClick.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickUpdateOnClick.java new file mode 100644 index 0000000..fdfe12e --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceClickUpdateOnClick.java @@ -0,0 +1,36 @@ +package net.infumia.frame.pipeline.service.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.element.ContextElementClick; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickUpdateOnClick + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickUpdateOnClick(); + + public static final String KEY = "update-on-click"; + + @Override + public String key() { + return ServiceClickUpdateOnClick.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextElement.Click ctx) { + final ContextElementClick context = ctx.context(); + final Element element = context.element(); + if (element.updateOnClick()) { + return ((ElementRich) element).pipelines().executeUpdate(context, false); + } + return CompletableFuture.completedFuture(State.CONTINUE); + } + + private ServiceClickUpdateOnClick() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRender.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRender.java new file mode 100644 index 0000000..18e96dd --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRender.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.service.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandlerHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRender implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRender(); + + public static final String KEY = "render"; + + @Override + public String key() { + return ServiceRender.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextElement.Render ctx) { + final Element element = ctx.context().element(); + if (element instanceof ElementEventHandlerHolder) { + return ((ElementEventHandlerHolder) element).eventHandler().handleRender(ctx); + } + return CompletableFuture.completedFuture(State.CONTINUE); + } + + private ServiceRender() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRenderLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRenderLogging.java new file mode 100644 index 0000000..6e14fc2 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceRenderLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementRender; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRenderLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRenderLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceRenderLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Render ctx) { + final ContextElementRender context = ctx.context(); + final String key = ((ElementRich) context.element()).key(); + // TODO: portlek, Add more detailed message. + context.manager().logger().debug("Element '%s' is rendered.", key == null ? "null" : key); + } + + private ServiceRenderLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdate.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdate.java new file mode 100644 index 0000000..f397c49 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdate.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.service.element; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementEventHandlerHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceUpdate implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceUpdate(); + + public static final String KEY = "update"; + + @Override + public String key() { + return ServiceUpdate.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextElement.Update ctx) { + final Element element = ctx.context().element(); + if (element instanceof ElementEventHandlerHolder) { + return ((ElementEventHandlerHolder) element).eventHandler().handleUpdate(ctx); + } + return CompletableFuture.completedFuture(State.CONTINUE); + } + + private ServiceUpdate() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdateLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdateLogging.java new file mode 100644 index 0000000..fb61d94 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/element/ServiceUpdateLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.element; + +import net.infumia.frame.context.element.ContextElementUpdate; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextElement; +import org.jetbrains.annotations.NotNull; + +public final class ServiceUpdateLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceUpdateLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceUpdateLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextElement.Update ctx) { + final ContextElementUpdate context = ctx.context(); + final String key = ((ElementRich) context.element()).key(); + // TODO: portlek, Add more detailed message. + context.manager().logger().debug("Element '%s' is updated.", key == null ? "null" : key); + } + + private ServiceUpdateLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegistered.java b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegistered.java new file mode 100644 index 0000000..ba97e88 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegistered.java @@ -0,0 +1,28 @@ +package net.infumia.frame.pipeline.service.manager; + +import net.infumia.frame.FrameRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import org.jetbrains.annotations.NotNull; + +public final class ServiceListenerRegistered + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer< + PipelineContextManager.ListenerRegistered + > INSTANCE = new ServiceListenerRegistered(); + + public static final String KEY = "register"; + + @Override + public String key() { + return ServiceListenerRegistered.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextManager.ListenerRegistered ctx) { + ((FrameRich) ctx.frame()).listener().register(); + } + + private ServiceListenerRegistered() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegisteredLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegisteredLogging.java new file mode 100644 index 0000000..f5ace50 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceListenerRegisteredLogging.java @@ -0,0 +1,27 @@ +package net.infumia.frame.pipeline.service.manager; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import org.jetbrains.annotations.NotNull; + +public final class ServiceListenerRegisteredLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer< + PipelineContextManager.ListenerRegistered + > INSTANCE = new ServiceListenerRegisteredLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceListenerRegisteredLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextManager.ListenerRegistered ctx) { + ctx.frame().logger().debug("All the listeners are registered!"); + } + + private ServiceListenerRegisteredLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewCreated.java b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewCreated.java new file mode 100644 index 0000000..a3a9872 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewCreated.java @@ -0,0 +1,47 @@ +package net.infumia.frame.pipeline.service.manager; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import org.jetbrains.annotations.NotNull; + +public final class ServiceViewCreated + implements PipelineService> { + + public static final PipelineService< + PipelineContextManager.ViewCreated, + Collection + > INSTANCE = new ServiceViewCreated(); + + public static final String KEY = "create"; + + @Override + public String key() { + return ServiceViewCreated.KEY; + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public CompletableFuture> handle( + @NotNull final PipelineContextManager.ViewCreated ctx + ) { + final CompletableFuture[] created = ctx + .registeredViews() + .stream() + .map(ctx.frame().viewCreator()::create) + .toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(created).thenApply(__ -> { + final Collection views = Arrays.stream(created) + .map(CompletableFuture::join) + .collect(Collectors.toSet()); + ctx.frame().logger().debug("View classes are created '%s'", ctx.registeredViews()); + return views; + }); + } + + private ServiceViewCreated() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewRegistered.java b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewRegistered.java new file mode 100644 index 0000000..604428a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewRegistered.java @@ -0,0 +1,66 @@ +package net.infumia.frame.pipeline.service.manager; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import net.infumia.frame.Frame; +import net.infumia.frame.config.ViewConfigBuilderRich; +import net.infumia.frame.context.ContextImpl; +import net.infumia.frame.context.view.ContextInitImpl; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import net.infumia.frame.state.StateRegistry; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewEventHandler; +import net.infumia.frame.view.ViewImpl; +import org.jetbrains.annotations.NotNull; + +public final class ServiceViewRegistered + implements PipelineService> { + + public static final PipelineService< + PipelineContextManager.ViewRegistered, + Collection + > INSTANCE = new ServiceViewRegistered(); + + public static final String KEY = "register"; + + @Override + public String key() { + return ServiceViewRegistered.KEY; + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public CompletableFuture> handle( + @NotNull final PipelineContextManager.ViewRegistered ctx + ) { + final Frame frame = ctx.frame(); + final CompletableFuture[] futures = ctx + .registeredViews() + .stream() + .map(instance -> { + final View view = new ViewImpl( + new ContextInitImpl( + new ContextImpl( + frame, + frame.storageFactory().create(new ConcurrentHashMap<>()), + new StateRegistry(frame.logger()) + ), + ViewConfigBuilderRich.create() + ), + instance + ); + return ((ViewEventHandler) view).simulateOnInit().thenApply(state -> view); + }) + .toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(futures).thenApply(__ -> + Arrays.stream(futures).map(CompletableFuture::join).collect(Collectors.toSet()) + ); + } + + private ServiceViewRegistered() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewUnregisteredLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewUnregisteredLogging.java new file mode 100644 index 0000000..0a45a51 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/manager/ServiceViewUnregisteredLogging.java @@ -0,0 +1,26 @@ +package net.infumia.frame.pipeline.service.manager; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextManager; +import org.jetbrains.annotations.NotNull; + +public final class ServiceViewUnregisteredLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceViewUnregisteredLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceViewUnregisteredLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextManager.ViewUnregistered ctx) { + ctx.frame().logger().debug("View classes are unregistered '%s'", ctx.unregisteredViews()); + } + + private ServiceViewUnregisteredLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRender.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRender.java new file mode 100644 index 0000000..8f1fc7c --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRender.java @@ -0,0 +1,44 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRender + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRender(); + + public static final String KEY = "render"; + + @Override + public String key() { + return ServiceFirstRender.KEY; + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public CompletableFuture handle(@NotNull final PipelineContextRender.FirstRender ctx) { + final List elements = ctx.elements(); + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + for (final Element element : elements) { + context.addElement(element); + } + final int size = elements.size(); + final CompletableFuture[] futures = new CompletableFuture[size]; + for (int i = size; i > 0; i--) { + final ElementRich element = (ElementRich) elements.get(i - 1); + futures[size - i] = element.pipelines().executeRender(context); + } + return CompletableFuture.allOf(futures).thenApply(unused -> State.CONTINUE); + } + + private ServiceFirstRender() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderAvailableSlotResolution.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderAvailableSlotResolution.java new file mode 100644 index 0000000..f5c4600 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderAvailableSlotResolution.java @@ -0,0 +1,122 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.element.ElementBuilderRich; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.view.ViewContainerRich; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderAvailableSlotResolution + implements PipelineServiceConsumer { + + private static final char AVAILABLE_SLOT = ' '; + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderAvailableSlotResolution(); + + public static final String KEY = "available-slot-resolution"; + + @Override + public String key() { + return ServiceFirstRenderAvailableSlotResolution.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + if (ctx.context().layouts().isEmpty()) { + this.fromInitialSlot(ctx); + } else { + this.fromAvailableSlots(ctx); + } + } + + private void fromAvailableSlots(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + final Map layouts = context.layouts(); + final LayoutSlot layout = layouts.get( + ServiceFirstRenderAvailableSlotResolution.AVAILABLE_SLOT + ); + if (layout == null) { + return; + } + final int[] slots = layout.slots(); + if (slots.length == 0) { + return; + } + final List> availableSlotFinders = context + .slotFinder() + .availableSlotFinders(); + int offset = 0; + for (int i = 0; i < availableSlotFinders.size(); i++) { + int slot; + try { + slot = slots[i + offset]; + } catch (final IndexOutOfBoundsException e) { + throw new RuntimeException( + "Capacity to accommodate items in the layout for items in available slots has been exceeded." + ); + } + while (this.isSlotNotAvailableForAutoFilling(context, slot)) { + try { + slot = slots[i + ++offset]; + } catch (final IndexOutOfBoundsException exception) { + throw new RuntimeException( + String.format( + "Capacity to accommodate items in the layout for items" + + " in available slots has been exceeded. " + + "Tried to set an item from index %d from position %d to another, " + + "but it breaks the layout rules", + i, + slot + ) + ); + } + } + ctx.addElement( + ((ElementBuilderRich) availableSlotFinders.get(i).apply(i, slot)).build(context) + ); + } + } + + private void fromInitialSlot(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + final List> availableSlotFinders = context + .slotFinder() + .availableSlotFinders(); + int slot = 0; + for (int i = 0; i < context.container().size(); i++) { + while (this.isSlotNotAvailableForAutoFilling(context, slot)) { + slot++; + } + if (i >= availableSlotFinders.size()) { + break; + } + final BiFunction factory = + availableSlotFinders.get(i); + ctx.addElement(((ElementBuilderRich) factory.apply(i, slot++)).build(context)); + } + } + + private boolean isSlotNotAvailableForAutoFilling( + @NotNull final ContextRender context, + final int slot + ) { + return ( + !((ViewContainerRich) context.container()).typeRich().canPlayerInteractOn(slot) || + context.container().hasItem(slot) || + ((ContextRenderRich) context).slotFinder() + .nonRenderedBuilders() + .stream() + .anyMatch(builder -> builder.slot() == slot) + ); + } + + private ServiceFirstRenderAvailableSlotResolution() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderConsumeNonRenderedElement.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderConsumeNonRenderedElement.java new file mode 100644 index 0000000..63e8cd2 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderConsumeNonRenderedElement.java @@ -0,0 +1,37 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.Iterator; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.element.ElementItemBuilderRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderConsumeNonRenderedElement + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderConsumeNonRenderedElement(); + + public static final String KEY = "consume-non-rendered-element"; + + @Override + public String key() { + return ServiceFirstRenderConsumeNonRenderedElement.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + final Iterator iterator = context + .slotFinder() + .nonRenderedBuilders() + .iterator(); + while (iterator.hasNext()) { + ctx.addElement(iterator.next().build(context)); + iterator.remove(); + } + } + + private ServiceFirstRenderConsumeNonRenderedElement() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderInitializeState.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderInitializeState.java new file mode 100644 index 0000000..2aeac49 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderInitializeState.java @@ -0,0 +1,39 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.state.value.StateValue; +import net.infumia.frame.state.value.StateValueHostRich; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderInitializeState + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderInitializeState(); + + public static final String KEY = "initialize-state"; + + @Override + public String key() { + return ServiceFirstRenderInitializeState.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRender context = ctx.context(); + final StateValueHostRich host = (StateValueHostRich) context.stateValueHost(); + host + .stateValues() + .forEach((state, value) -> { + final StateValue newValue = state.valueFactory().apply(context, state); + if (newValue.mutable()) { + newValue.value(value); + } + host.initializeState(state, newValue); + }); + } + + private ServiceFirstRenderInitializeState() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLayout.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLayout.java new file mode 100644 index 0000000..396ec4b --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLayout.java @@ -0,0 +1,45 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.function.IntFunction; +import java.util.stream.IntStream; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.ElementBuilderRich; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.slot.LayoutSlot; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderLayout + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderLayout(); + + public static final String KEY = "layout"; + + @Override + public String key() { + return ServiceFirstRenderLayout.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRender context = ctx.context(); + for (final LayoutSlot layout : context.layouts().values()) { + final IntFunction builderFactory = layout.builderFactory(); + if (builderFactory == null) { + continue; + } + final int[] slots = layout.slots(); + IntStream.range(0, slots.length) + .mapToObj(index -> + (ElementBuilderRich) builderFactory.apply(index).slot(slots[index]) + ) + .map(element -> element.build(context)) + .forEach(ctx::addElement); + } + } + + private ServiceFirstRenderLayout() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLogging.java new file mode 100644 index 0000000..7a4e0c5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderLogging.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceFirstRenderLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + ctx + .context() + .manager() + .logger() + .debug( + "onFirstRender ran successfully for view '%s'.", + ctx.context().view().instance() + ); + } + + private ServiceFirstRenderLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderOnFirstRender.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderOnFirstRender.java new file mode 100644 index 0000000..340e4cc --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderOnFirstRender.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderOnFirstRender + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderOnFirstRender(); + + public static final String KEY = "on-first-render"; + + @Override + public String key() { + return ServiceFirstRenderOnFirstRender.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + final Object instance = ctx.context().view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onFirstRender(ctx.context()); + } + } + + private ServiceFirstRenderOnFirstRender() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderPagination.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderPagination.java new file mode 100644 index 0000000..cd6b603 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderPagination.java @@ -0,0 +1,43 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.element.pagination.ElementPagination; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.state.State; +import net.infumia.frame.state.pagination.StatePagination; +import org.jetbrains.annotations.NotNull; + +public final class ServiceFirstRenderPagination + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderPagination(); + + public static final String KEY = "pagination"; + + @Override + public String key() { + return ServiceFirstRenderPagination.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + final Collection> futures = new ArrayList<>(); + for (final net.infumia.frame.state.State state : context.stateRegistry()) { + if (state instanceof StatePagination) { + futures.add(((StatePagination) state).getOtThrowWait(context)); + } + } + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenRun(() -> futures.stream().map(CompletableFuture::join).forEach(ctx::addElement)) + .thenApply(__ -> State.CONTINUE); + } + + private ServiceFirstRenderPagination() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderWatchState.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderWatchState.java new file mode 100644 index 0000000..a5bc3d5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceFirstRenderWatchState.java @@ -0,0 +1,91 @@ +package net.infumia.frame.pipeline.service.render; + +import java.util.Collection; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementContainer; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +// TODO: portlek, Add more detailed messages for the errors below. +public final class ServiceFirstRenderWatchState + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceFirstRenderWatchState(); + + public static final String KEY = "watch-state"; + + @Override + public String key() { + return ServiceFirstRenderWatchState.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.FirstRender ctx) { + final ContextRender context = ctx.context(); + for (final Element element : ctx.elements()) { + ServiceFirstRenderWatchState.watch(context, (ElementRich) element); + } + } + + private static void watch( + @NotNull final ContextRender context, + @NotNull final ElementRich element + ) { + ServiceFirstRenderWatchState.updateOnStateAccess(context, element); + ServiceFirstRenderWatchState.updateOnStateChange(context, element); + if (element instanceof ElementContainer) { + for (final Element child : ((ElementContainer) element).elements()) { + ServiceFirstRenderWatchState.watch(context, (ElementRich) child); + } + } + } + + private static void updateOnStateAccess( + @NotNull final ContextRender context, + @NotNull final ElementRich element + ) { + final Collection> states = element.updateOnStateAccess(); + if (states == null) { + return; + } + for (final net.infumia.frame.state.State state : states) { + state.watchAccess(context, update -> + CompletableFutureExtensions.logError( + element.pipelines().executeUpdate(context, false), + context.manager().logger(), + "An error occurred while updating element '%s' due to state '%s' access!", + element.key(), + state + ) + ); + } + } + + private static void updateOnStateChange( + @NotNull final ContextRender context, + @NotNull final ElementRich element + ) { + final Collection> states = element.updateOnStateChange(); + if (states == null) { + return; + } + for (final net.infumia.frame.state.State state : states) { + state.watchUpdate(context, update -> + CompletableFutureExtensions.logError( + element.pipelines().executeUpdate(context, false), + context.manager().logger(), + "An error occurred while updating element '%s' due to state '%s' change!", + element.key(), + state + ) + ); + } + } + + private ServiceFirstRenderWatchState() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainer.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainer.java new file mode 100644 index 0000000..2e5c295 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainer.java @@ -0,0 +1,35 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenContainer + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenContainer(); + + public static final String KEY = "open-container"; + + @Override + public String key() { + return ServiceOpenContainer.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.OpenContainer ctx) { + for (final Viewer viewer : ctx.viewers()) { + viewer + .metadata() + .getOrThrow(MetadataKeyHolder.CONTEXTUAL_VIEWER) + .context() + .container() + .open(viewer); + } + } + + private ServiceOpenContainer() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainerLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainerLogging.java new file mode 100644 index 0000000..75e2e10 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceOpenContainerLogging.java @@ -0,0 +1,27 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenContainerLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenContainerLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceOpenContainerLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.OpenContainer ctx) { + // TODO: portlek, More detailed message. + ctx.context().manager().logger().debug("Container opened for viewers '%s'.", ctx.viewers()); + } + + private ServiceOpenContainerLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeLogging.java new file mode 100644 index 0000000..faabed9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeLogging.java @@ -0,0 +1,36 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.context.view.ContextResume; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceResumeLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceResumeLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceResumeLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.Resume ctx) { + // TODO: portlek, More detailed message. + final ContextResume context = ctx.context(); + context + .manager() + .logger() + .debug( + "Viewers '%s' resumed from view '%s'.", + context.resumedViewers(), + context.from().view().instance() + ); + } + + private ServiceResumeLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeOnResume.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeOnResume.java new file mode 100644 index 0000000..01d8167 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceResumeOnResume.java @@ -0,0 +1,32 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.context.view.ContextResume; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceResumeOnResume + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceResumeOnResume(); + + public static final String KEY = "on-resume"; + + @Override + public String key() { + return ServiceResumeOnResume.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.Resume ctx) { + final ContextResume context = ctx.context(); + final Object instance = context.view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onResume(context); + } + } + + private ServiceResumeOnResume() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdate.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdate.java new file mode 100644 index 0000000..0e1c771 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdate.java @@ -0,0 +1,52 @@ +package net.infumia.frame.pipeline.service.render; + +import java.time.Duration; +import net.infumia.frame.Frame; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.extension.CompletableFutureExtensions; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStartUpdate + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStartUpdate(); + + public static final String KEY = "start-update"; + + @Override + public String key() { + return ServiceStartUpdate.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StartUpdate ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + final Duration updateInterval = Preconditions.stateNotNull( + context.config().updateInterval(), + "Update interval cannot be null in start-update service!" + ); + + final Frame manager = context.manager(); + context.updateTask( + manager + .taskFactory() + .sync( + () -> + CompletableFutureExtensions.logError( + context.pipelines().executeUpdate(), + manager.logger(), + "An error occurred while running the update task of view '%s'.", + context.view().instance() + ), + updateInterval, + updateInterval + ) + ); + } + + private ServiceStartUpdate() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateCancel.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateCancel.java new file mode 100644 index 0000000..b4e7546 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateCancel.java @@ -0,0 +1,38 @@ +package net.infumia.frame.pipeline.service.render; + +import java.time.Duration; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStartUpdateCancel + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStartUpdateCancel(); + + public static final String KEY = "cancel"; + + @Override + public String key() { + return ServiceStartUpdateCancel.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StartUpdate ctx) { + final ContextRender context = ctx.context(); + final Duration updateInterval = context.config().updateInterval(); + + if ( + updateInterval == null || + updateInterval.isNegative() || + updateInterval.isZero() || + context.viewers().isEmpty() + ) { + ctx.cancelled(true); + } + } + + private ServiceStartUpdateCancel() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateInvalidate.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateInvalidate.java new file mode 100644 index 0000000..7f69aa0 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateInvalidate.java @@ -0,0 +1,47 @@ +package net.infumia.frame.pipeline.service.render; + +import java.io.Closeable; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStartUpdateInvalidate + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStartUpdateInvalidate(); + + public static final String KEY = "invalidate"; + + @Override + public String key() { + return ServiceStartUpdateInvalidate.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StartUpdate ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + + final Closeable oldTask = context.updateTask(); + if (oldTask == null) { + return; + } + + try { + oldTask.close(); + context.updateTask(null); + } catch (final Throwable e) { + context + .manager() + .logger() + .error( + e, + "An error occurred while closing the old update task of view '%s'.", + context.view().instance() + ); + } + } + + private ServiceStartUpdateInvalidate() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateLogging.java new file mode 100644 index 0000000..ff473ce --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStartUpdateLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStartUpdateLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStartUpdateLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceStartUpdateLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StartUpdate ctx) { + // TODO: portlek, More detailed message. + ctx + .context() + .manager() + .logger() + .debug("Update task started for view '%s'.", ctx.context().view().instance()); + } + + private ServiceStartUpdateLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdate.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdate.java new file mode 100644 index 0000000..f029c81 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdate.java @@ -0,0 +1,35 @@ +package net.infumia.frame.pipeline.service.render; + +import java.io.Closeable; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStopUpdate + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStopUpdate(); + + public static final String KEY = "stop-update"; + + @Override + public String key() { + return ServiceStopUpdate.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StopUpdate ctx) { + final Closeable updateTask = ((ContextRenderRich) ctx.context()).updateTask(); + if (updateTask != null) { + try { + updateTask.close(); + } catch (final Throwable e) { + throw new RuntimeException("An error occurred while closing update task!", e); + } + } + } + + private ServiceStopUpdate() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdateLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdateLogging.java new file mode 100644 index 0000000..1e0cf11 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceStopUpdateLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceStopUpdateLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceStopUpdateLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceStopUpdateLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.StopUpdate ctx) { + // TODO: portlek, More detailed message. + ctx + .context() + .manager() + .logger() + .debug("Update task stopped for view '%s'.", ctx.context().view().instance()); + } + + private ServiceStopUpdateLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateLogging.java new file mode 100644 index 0000000..9d11033 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateLogging.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ServiceUpdateLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceUpdateLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceUpdateLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.Update ctx) { + // TODO: portlek, More detailed message. + ctx + .context() + .manager() + .logger() + .debug("View '%s' updated.", ctx.context().view().instance()); + } + + private ServiceUpdateLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateOnUpdate.java b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateOnUpdate.java new file mode 100644 index 0000000..3090caf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/render/ServiceUpdateOnUpdate.java @@ -0,0 +1,32 @@ +package net.infumia.frame.pipeline.service.render; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextRender; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceUpdateOnUpdate + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceUpdateOnUpdate(); + + public static final String KEY = "on-update"; + + @Override + public String key() { + return ServiceUpdateOnUpdate.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextRender.Update ctx) { + final ContextRender context = ctx.context(); + final Object instance = context.view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onUpdate(context); + } + } + + private ServiceUpdateOnUpdate() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceAccessLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceAccessLogging.java new file mode 100644 index 0000000..5f3b2c8 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceAccessLogging.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.state; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextState; +import org.jetbrains.annotations.NotNull; + +public final class ServiceAccessLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceAccessLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceAccessLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextState.Access ctx) { + final Object value = ctx.value().value(); + ctx + .manager() + .logger() + .debug( + "State '%s' has been accessed for value '%s'.", + ctx.state(), + value == null ? "null" : value + ); + } + + private ServiceAccessLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceUpdateLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceUpdateLogging.java new file mode 100644 index 0000000..21886c8 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/state/ServiceUpdateLogging.java @@ -0,0 +1,36 @@ +package net.infumia.frame.pipeline.service.state; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextState; +import org.jetbrains.annotations.NotNull; + +public final class ServiceUpdateLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceUpdateLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceUpdateLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextState.Update ctx) { + final Object oldValue = ctx.oldValue(); + final Object newValue = ctx.value().value(); + ctx + .manager() + .logger() + .debug( + "State '%s' has been updated from '%s' to '%s'.", + ctx.state(), + oldValue == null ? "null" : oldValue, + newValue == null ? "null" : newValue + ); + } + + private ServiceUpdateLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickCancel.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickCancel.java new file mode 100644 index 0000000..3edfbd3 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickCancel.java @@ -0,0 +1,32 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.config.ViewConfigRich; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.config.option.ViewConfigOptions; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickCancel + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickCancel(); + + public static final String KEY = "cancel"; + + @Override + public String key() { + return ServiceClickCancel.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Click ctx) { + final ContextClick context = ctx.context(); + ((ViewConfigRich) context.config()).option(ViewConfigOptions.CANCEL_ON_CLICK) + .filter(cancel -> cancel) + .ifPresent(__ -> context.cancelled(true)); + } + + private ServiceClickCancel() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickElement.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickElement.java new file mode 100644 index 0000000..ac1d95a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickElement.java @@ -0,0 +1,45 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.element.Element; +import net.infumia.frame.element.ElementRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.Pipelined; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickElement + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickElement(); + + public static final String KEY = "element"; + + @Override + public String key() { + return ServiceClickElement.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextView.Click ctx) { + final ContextClick context = ctx.context(); + final int clickedSlot = context.clickedSlotRaw(); + final List elements = context.elements(); + final Optional found = elements + .stream() + .map(ElementRich.class::cast) + .filter(e -> e.containedWithin(clickedSlot)) + .findFirst(); + return found + .map(Pipelined::pipelines) + .map(pipelines -> pipelines.executeClick(context)) + .orElseGet(() -> CompletableFuture.completedFuture(State.CONTINUE)); + } + + private ServiceClickElement() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickInteractionDelay.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickInteractionDelay.java new file mode 100644 index 0000000..28f668f --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickInteractionDelay.java @@ -0,0 +1,62 @@ +package net.infumia.frame.pipeline.service.view; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickInteractionDelay + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickInteractionDelay(); + + public static final String KEY = "interaction-delay"; + + @Override + public String key() { + return ServiceClickInteractionDelay.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Click ctx) { + final ContextClick context = ctx.context(); + final Duration delay = context.config().interactionDelay(); + if (delay == null || delay.isZero() || delay.isNegative()) { + return; + } + final Viewer clicker = context.clicker(); + final MetadataAccess metadata = clicker.metadata(); + Map lastInteractions = metadata.get(MetadataKeyHolder.LAST_INTERACTION); + if (lastInteractions == null) { + lastInteractions = new HashMap<>(); + metadata.setFixed(MetadataKeyHolder.LAST_INTERACTION, lastInteractions); + } + final String key = context.id().toString(); + final Long lastInteraction = lastInteractions.get(key); + final long now = System.currentTimeMillis(); + if (lastInteraction == null) { + lastInteractions.put(key, now); + return; + } + final long elapsed = now - lastInteraction; + if (elapsed > delay.toMillis()) { + lastInteractions.put(key, now); + } else { + context + .manager() + .logger() + .debug("Click event of viewer '%s' cancelled due to interaction delay.", clicker); + ctx.cancelled(true); + context.cancelled(true); + } + } + + private ServiceClickInteractionDelay() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickLogging.java new file mode 100644 index 0000000..af06747 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickLogging.java @@ -0,0 +1,36 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceClickLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Click ctx) { + // TODO: portlek, More detailed message. + final ContextClick context = ctx.context(); + context + .manager() + .logger() + .debug( + "Player '%s' clicked on view '%s'.", + context.clicker(), + context.view().instance() + ); + } + + private ServiceClickLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickOnClick.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickOnClick.java new file mode 100644 index 0000000..2b23973 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClickOnClick.java @@ -0,0 +1,32 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextClick; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClickOnClick + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClickOnClick(); + + public static final String KEY = "on-click"; + + @Override + public String key() { + return ServiceClickOnClick.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Click ctx) { + final ContextClick context = ctx.context(); + final Object instance = context.view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onClick(context); + } + } + + private ServiceClickOnClick() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClose.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClose.java new file mode 100644 index 0000000..6f1517a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceClose.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceClose implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceClose(); + + public static final String KEY = "close"; + + @Override + public String key() { + return ServiceClose.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextView.Close ctx) { + final ContextClose context = ctx.context(); + return context.pipelinesViewer().executeRemoved(Collections.singleton(context.viewer())); + } + + private ServiceClose() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseCancel.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseCancel.java new file mode 100644 index 0000000..fec452e --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseCancel.java @@ -0,0 +1,46 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCloseCancel + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceCloseCancel(); + + public static final String KEY = "cancel"; + + @Override + public String key() { + return ServiceCloseCancel.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Close ctx) { + final ContextClose context = ctx.context(); + final Viewer viewer = context.viewer(); + if (!context.viewer().player().isOnline()) { + ctx.cancelled(true); + return; + } + if (!context.cancelled() || context.forced()) { + return; + } + ctx.cancelled(true); + context.manager().taskFactory().sync(() -> viewer.open(context.container())); + final Player player = viewer.player(); + final ItemStack cursor = player.getItemOnCursor(); + if (cursor != null && cursor.getType() != Material.AIR) { + player.setItemOnCursor(cursor); + } + } + + private ServiceCloseCancel() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseLogging.java new file mode 100644 index 0000000..89e7930 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseLogging.java @@ -0,0 +1,35 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextClose; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCloseLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceCloseLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceCloseLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Close ctx) { + final ContextClose context = ctx.context(); + context + .manager() + .logger() + .debug( + "View '%s' closed for viewer '%s'.", + context.view().instance(), + context.viewer() + ); + } + + private ServiceCloseLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseOnClose.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseOnClose.java new file mode 100644 index 0000000..a56c9a9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCloseOnClose.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCloseOnClose + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceCloseOnClose(); + + public static final String KEY = "on-close"; + + @Override + public String key() { + return ServiceCloseOnClose.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Close ctx) { + final Object instance = ctx.context().view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onClose(ctx.context()); + } + } + + private ServiceCloseOnClose() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContainer.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContainer.java new file mode 100644 index 0000000..d81a846 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContainer.java @@ -0,0 +1,81 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import net.infumia.frame.InvTypeRich; +import net.infumia.frame.InvTypes; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.type.InvType; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.ViewContainer; +import net.infumia.frame.view.ViewContainerImpl; +import net.infumia.frame.view.config.ViewConfig; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCreateContainer + implements PipelineService { + + private static final Collection EXTENDABLE = Arrays.stream(InvType.VALUES) + .map(InvTypes::fromType) + .filter(InvTypeRich::extendable) + .collect(Collectors.toSet()); + + public static final PipelineService< + PipelineContextView.CreateContainer, + ViewContainer + > INSTANCE = new ServiceCreateContainer(); + + public static final String KEY = "create"; + + @Override + public String key() { + return ServiceCreateContainer.KEY; + } + + @NotNull + @Override + public CompletableFuture handle( + @NotNull final PipelineContextView.CreateContainer ctx + ) { + final ContextBase context = ctx.context(); + final Object instance = context.view().instance(); + final ViewConfig config = ctx.config(); + final InvType type = config.type(); + final InvTypeRich typeRich = InvTypes.fromType(type); + final InventoryType inventoryType = typeRich.toInventoryType(); + Preconditions.argumentNotNull(inventoryType, "%s view type is not supported!", type); + final int normalized = typeRich.normalize(config.size()); + Preconditions.argument( + normalized == 0 || typeRich.extendable(), + "Only '%s' type(s) can have a custom size, '%s' always have a size of %d. Remove the part that specifies the size of the container on %s or just set the type explicitly.", + ServiceCreateContainer.EXTENDABLE.stream() + .map(InvTypeRich::type) + .collect(Collectors.toSet()), + type, + typeRich.maxSize(), + instance + ); + return CompletableFuture.completedFuture( + new ViewContainerImpl( + context + .manager() + .inventoryCreator() + .create( + instance instanceof InventoryHolder ? (InventoryHolder) instance : null, + inventoryType, + normalized, + config.title() + ), + typeRich + ) + ); + } + + private ServiceCreateContainer() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContext.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContext.java new file mode 100644 index 0000000..8987c1d --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateContext.java @@ -0,0 +1,51 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.config.ViewConfigBuilderRich; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.ContextBaseImpl; +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.View; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCreateContext + implements PipelineService { + + public static final PipelineService INSTANCE = + new ServiceCreateContext(); + + public static final String KEY = "create"; + + @Override + public String key() { + return ServiceCreateContext.KEY; + } + + @NotNull + @Override + public CompletableFuture handle( + @NotNull final PipelineContextView.CreateContext ctx + ) { + final View view = ctx.view(); + final ContextInit context = view.context(); + final Collection viewers = ctx.viewers(); + return CompletableFuture.completedFuture( + new ContextBaseImpl( + context, + UUID.randomUUID(), + view, + ((ViewConfigBuilderRich) context.configBuilder()).build(), + viewers, + ctx.initialData(), + viewers.size() == 1 ? viewers.iterator().next() : null + ) + ); + } + + private ServiceCreateContext() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateRender.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateRender.java new file mode 100644 index 0000000..e37160e --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateRender.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderImpl; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCreateRender + implements PipelineService { + + public static final PipelineService INSTANCE = + new ServiceCreateRender(); + + public static final String KEY = "create"; + + @Override + public String key() { + return ServiceCreateRender.KEY; + } + + @NotNull + @Override + public CompletableFuture handle( + @NotNull final PipelineContextView.CreateRender ctx + ) { + return CompletableFuture.completedFuture( + new ContextRenderImpl(ctx.context(), ctx.container(), ctx.config(), ctx.layouts()) + ); + } + + private ServiceCreateRender() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateViewers.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateViewers.java new file mode 100644 index 0000000..7597218 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceCreateViewers.java @@ -0,0 +1,45 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import net.infumia.frame.pipeline.PipelineService; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.View; +import net.infumia.frame.viewer.Viewer; +import net.infumia.frame.viewer.ViewerCreator; +import org.jetbrains.annotations.NotNull; + +public final class ServiceCreateViewers + implements PipelineService> { + + public static final PipelineService< + PipelineContextView.CreateViewers, + Collection + > INSTANCE = new ServiceCreateViewers(); + + public static final String KEY = "create"; + + @Override + public String key() { + return ServiceCreateViewers.KEY; + } + + @NotNull + @Override + public CompletableFuture> handle( + @NotNull final PipelineContextView.CreateViewers ctx + ) { + final View view = ctx.view(); + final ViewerCreator viewerCreator = view.context().manager().viewerCreator(); + return CompletableFuture.completedFuture( + ctx + .viewers() + .stream() + .map(player -> viewerCreator.create(player, view)) + .collect(Collectors.toSet()) + ); + } + + private ServiceCreateViewers() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitLogging.java new file mode 100644 index 0000000..fdf870a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitLogging.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceInitLogging implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceInitLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceInitLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Init ctx) { + ctx + .view() + .context() + .manager() + .logger() + .debug("onInit ran successfully for view '%s'.", ctx.view().instance()); + } + + private ServiceInitLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitOnInit.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitOnInit.java new file mode 100644 index 0000000..ab10fdf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitOnInit.java @@ -0,0 +1,29 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceInitOnInit implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceInitOnInit(); + + public static final String KEY = "on-init"; + + @Override + public String key() { + return ServiceInitOnInit.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Init ctx) { + final Object instance = ctx.view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onInit(ctx.view().context()); + } + } + + private ServiceInitOnInit() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitWaitUntil.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitWaitUntil.java new file mode 100644 index 0000000..95cfa1e --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceInitWaitUntil.java @@ -0,0 +1,41 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceInitWaitUntil + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceInitWaitUntil(); + + public static final String KEY = "wait-until"; + + @Override + public String key() { + return ServiceInitWaitUntil.KEY; + } + + @Override + public void accept( + @NotNull final CompletableFuture future, + @NotNull final PipelineContextView.Init ctx + ) { + final CompletableFuture waitUntil = ctx.view().context().waitUntil(); + if (waitUntil == null) { + future.complete(State.CONTINUE); + } else { + waitUntil.whenComplete((__, throwable) -> { + if (throwable == null) { + future.complete(State.CONTINUE); + } else { + future.completeExceptionally(throwable); + } + }); + } + } + + private ServiceInitWaitUntil() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolution.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolution.java new file mode 100644 index 0000000..586a3f6 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolution.java @@ -0,0 +1,67 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.ViewContainer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ServiceLayoutResolution + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceLayoutResolution(); + + public static final String KEY = "layout-resolution"; + + @Override + public String key() { + return ServiceLayoutResolution.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.LayoutResolution ctx) { + final String@Nullable[] layout = ctx.config().layout(); + if (layout == null) { + return; + } + final int layoutRows = layout.length; + if (layoutRows == 0) { + return; + } + final ViewContainer container = ctx.container(); + final int containerRows = container.rowsCount(); + Preconditions.state( + containerRows == layoutRows, + "Layout length (%d) must respect the rows count of the container (%d).", + layoutRows, + containerRows + ); + final Map> layouts = new HashMap<>(); + final int containerColumns = container.columnsCount(); + for (int row = 0; row < containerRows; row++) { + final String layer = layout[row]; + final int layerLength = layer.length(); + Preconditions.state( + containerColumns == layerLength, + "Layout layer length located at %d must respect the columns count of the container (given: %d, expect: %d).", + row, + layerLength, + containerColumns + ); + for (int column = 0; column < containerColumns; column++) { + layouts + .computeIfAbsent(layer.charAt(column), ArrayList::new) + .add(column + row * containerColumns); + } + } + layouts.forEach(ctx::addLayout); + } + + private ServiceLayoutResolution() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolutionLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolutionLogging.java new file mode 100644 index 0000000..51387bd --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceLayoutResolutionLogging.java @@ -0,0 +1,26 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceLayoutResolutionLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceLayoutResolutionLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceLayoutResolutionLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.LayoutResolution ctx) { + ctx.context().manager().logger().debug("Layout '%s' resolved.", ctx.layouts()); + } + + private ServiceLayoutResolutionLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceModifyContainerLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceModifyContainerLogging.java new file mode 100644 index 0000000..7ac1678 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceModifyContainerLogging.java @@ -0,0 +1,26 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceModifyContainerLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceModifyContainerLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceModifyContainerLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.ModifyContainer ctx) { + ctx.context().manager().logger().debug("View container successfully modified."); + } + + private ServiceModifyContainerLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenInitializeState.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenInitializeState.java new file mode 100644 index 0000000..4b6851f --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenInitializeState.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.ContextBaseRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.state.StateRich; +import net.infumia.frame.state.value.StateValueHostRich; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenInitializeState + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenInitializeState(); + + public static final String KEY = "initialize-state"; + + @Override + public String key() { + return ServiceOpenInitializeState.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Open ctx) { + final ContextBaseRich context = (ContextBaseRich) ctx.context(); + final StateValueHostRich host = (StateValueHostRich) context.stateValueHost(); + for (final StateRich state : context.stateRegistry()) { + host.initializeState(state, state.valueFactory().apply(context, state)); + } + } + + private ServiceOpenInitializeState() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenLogging.java new file mode 100644 index 0000000..95daa2e --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenLogging.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenLogging implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceOpenLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Open ctx) { + final ContextOpen context = ctx.context(); + context + .manager() + .logger() + .debug("View '%s' is opening for players '%s'", context.view(), context.viewers()); + } + + private ServiceOpenLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenOnOpen.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenOnOpen.java new file mode 100644 index 0000000..c449047 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenOnOpen.java @@ -0,0 +1,29 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.ViewHandler; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenOnOpen implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenOnOpen(); + + public static final String KEY = "on-open"; + + @Override + public String key() { + return ServiceOpenOnOpen.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Open ctx) { + final Object instance = ctx.context().view().instance(); + if (instance instanceof ViewHandler) { + ((ViewHandler) instance).onOpen(ctx.context()); + } + } + + private ServiceOpenOnOpen() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenPreviousView.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenPreviousView.java new file mode 100644 index 0000000..60e52ee --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenPreviousView.java @@ -0,0 +1,45 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.Deque; +import java.util.LinkedList; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.viewer.ContextualViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenPreviousView + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenPreviousView(); + + public static final String KEY = "previous-view"; + + @Override + public String key() { + return ServiceOpenPreviousView.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Open ctx) { + for (final Viewer viewer : ctx.context().viewers()) { + final MetadataAccess metadata = viewer.metadata(); + final ContextualViewer oldContext = metadata.get(MetadataKeyHolder.CONTEXTUAL_VIEWER); + if (oldContext == null) { + continue; + } + Deque previousViews = metadata.get(MetadataKeyHolder.PREVIOUS_VIEWS); + if (previousViews == null) { + previousViews = new LinkedList<>(); + metadata.setFixed(MetadataKeyHolder.PREVIOUS_VIEWS, previousViews); + } + previousViews.add(oldContext.context()); + } + } + + private ServiceOpenPreviousView() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenWaitUntil.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenWaitUntil.java new file mode 100644 index 0000000..3d204ac --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceOpenWaitUntil.java @@ -0,0 +1,41 @@ +package net.infumia.frame.pipeline.service.view; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceOpenWaitUntil + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceOpenWaitUntil(); + + public static final String KEY = "wait-until"; + + @Override + public String key() { + return ServiceOpenWaitUntil.KEY; + } + + @Override + public void accept( + @NotNull final CompletableFuture future, + @NotNull final PipelineContextView.Open ctx + ) { + final CompletableFuture waitUntil = ctx.context().waitUntil(); + if (waitUntil == null) { + future.complete(State.CONTINUE); + } else { + waitUntil.whenComplete((result, throwable) -> { + if (throwable == null) { + future.complete(State.CONTINUE); + } else { + future.completeExceptionally(throwable); + } + }); + } + } + + private ServiceOpenWaitUntil() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifier.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifier.java new file mode 100644 index 0000000..2775cf4 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifier.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.view.config.ViewConfigBuilder; +import net.infumia.frame.view.config.ViewConfigModifier; +import org.jetbrains.annotations.NotNull; + +public final class ServiceProcessConfigModifier + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer< + PipelineContextView.ProcessConfigModifier + > INSTANCE = new ServiceProcessConfigModifier(); + + public static final String KEY = "process"; + + @Override + public String key() { + return ServiceProcessConfigModifier.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.ProcessConfigModifier ctx) { + final ContextOpen context = ctx.context(); + final ViewConfigBuilder configBuilder = context.modifyConfig(); + for (final ViewConfigModifier modifier : configBuilder.modifiers()) { + modifier.accept(configBuilder, context); + } + } + + private ServiceProcessConfigModifier() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierAddSizeModifier.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierAddSizeModifier.java new file mode 100644 index 0000000..ff789d5 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierAddSizeModifier.java @@ -0,0 +1,57 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ServiceProcessConfigModifierAddSizeModifier + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer< + PipelineContextView.ProcessConfigModifier + > INSTANCE = new ServiceProcessConfigModifierAddSizeModifier(); + + public static final String KEY = "add-size-modifier"; + + @Override + public String key() { + return ServiceProcessConfigModifierAddSizeModifier.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.ProcessConfigModifier ctx) { + ctx + .context() + .modifyConfig() + .addModifier((builder, context) -> { + final String@Nullable[] layout = builder.layout(); + if (layout == null) { + return; + } + final int layoutLength = layout.length; + if (layoutLength == 0) { + return; + } + final int size = builder.size(); + Preconditions.state( + size <= 0 || size == layoutLength, + "The layout length '%s' differs from the set inventory size '%s'!", + layoutLength, + size + ); + context + .manager() + .logger() + .debug( + "View's '%s' size modified according to the layout length '%s'.", + context.view().instance(), + layoutLength + ); + builder.size(layoutLength); + }); + } + + private ServiceProcessConfigModifierAddSizeModifier() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierLogging.java new file mode 100644 index 0000000..23271ee --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceProcessConfigModifierLogging.java @@ -0,0 +1,27 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import org.jetbrains.annotations.NotNull; + +public final class ServiceProcessConfigModifierLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer< + PipelineContextView.ProcessConfigModifier + > INSTANCE = new ServiceProcessConfigModifierLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceProcessConfigModifierLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.ProcessConfigModifier ctx) { + ctx.context().manager().logger().debug("Config modifiers are proceed."); + } + + private ServiceProcessConfigModifierLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransition.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransition.java new file mode 100644 index 0000000..7b790ef --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransition.java @@ -0,0 +1,36 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.viewer.ContextualViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceTransition + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceTransition(); + + public static final String KEY = "transition"; + + @Override + public String key() { + return ServiceTransition.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Transition ctx) { + for (final Viewer viewer : ctx.viewers()) { + final MetadataAccess metadata = viewer.metadata(); + final ContextualViewer oldContext = metadata.get(MetadataKeyHolder.CONTEXTUAL_VIEWER); + if (oldContext != null) { + metadata.setFixed(MetadataKeyHolder.TRANSITIONING_FROM, oldContext); + } + } + } + + private ServiceTransition() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransitionLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransitionLogging.java new file mode 100644 index 0000000..972c9c9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/view/ServiceTransitionLogging.java @@ -0,0 +1,48 @@ +package net.infumia.frame.pipeline.service.view; + +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextView; +import net.infumia.frame.viewer.ContextualViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceTransitionLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceTransitionLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceTransitionLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextView.Transition ctx) { + // TODO: portlek, More detailed message. + final ContextBase context = ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + final ContextualViewer transitioningFrom = viewer + .metadata() + .get(MetadataKeyHolder.TRANSITIONING_FROM); + if (transitioningFrom == null) { + continue; + } + context + .manager() + .logger() + .debug( + "Player '%s' is transitioning from view '%s' to view '%s'.", + viewer, + transitioningFrom.view().instance(), + context.view().instance() + ); + } + } + + private ServiceTransitionLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAdded.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAdded.java new file mode 100644 index 0000000..0a9b354 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAdded.java @@ -0,0 +1,30 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceAdded implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceAdded(); + + public static final String KEY = "added"; + + @Override + public String key() { + return ServiceAdded.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Added ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + context.addViewer(viewer); + } + } + + private ServiceAdded() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedContextualViewer.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedContextualViewer.java new file mode 100644 index 0000000..f39e9cd --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedContextualViewer.java @@ -0,0 +1,38 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.ContextualViewerImpl; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceAddedContextualViewer + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceAddedContextualViewer(); + + public static final String KEY = "contextual-viewer"; + + @Override + public String key() { + return ServiceAddedContextualViewer.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Added ctx) { + final ContextRender context = ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + viewer + .metadata() + .setFixed( + MetadataKeyHolder.CONTEXTUAL_VIEWER, + new ContextualViewerImpl(viewer, context) + ); + } + } + + private ServiceAddedContextualViewer() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedLogging.java new file mode 100644 index 0000000..3a234de --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedLogging.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceAddedLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceAddedLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceAddedLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Added ctx) { + final ContextRender context = ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + context + .manager() + .logger() + .debug("Viewer '%s' added to view '%s'.", viewer, context.view().instance()); + } + } + + private ServiceAddedLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedOnViewerAdded.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedOnViewerAdded.java new file mode 100644 index 0000000..e86302a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceAddedOnViewerAdded.java @@ -0,0 +1,37 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.view.ViewHandler; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceAddedOnViewerAdded + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceAddedOnViewerAdded(); + + public static final String KEY = "on-viewer-added"; + + @Override + public String key() { + return ServiceAddedOnViewerAdded.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Added ctx) { + final Object instance = ctx.context().view().instance(); + if (instance instanceof ViewHandler) { + final ViewHandler handler = (ViewHandler) instance; + for (final Viewer viewer : ctx.viewers()) { + handler.onViewerAdded( + viewer.metadata().getOrThrow(MetadataKeyHolder.CONTEXTUAL_VIEWER) + ); + } + } + } + + private ServiceAddedOnViewerAdded() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemoved.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemoved.java new file mode 100644 index 0000000..d7cd94a --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemoved.java @@ -0,0 +1,31 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRemoved + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRemoved(); + + public static final String KEY = "removed"; + + @Override + public String key() { + return ServiceRemoved.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Removed ctx) { + final ContextRenderRich context = (ContextRenderRich) ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + context.removeViewer(viewer); + } + } + + private ServiceRemoved() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedContextualViewer.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedContextualViewer.java new file mode 100644 index 0000000..e0b6742 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedContextualViewer.java @@ -0,0 +1,39 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRemovedContextualViewer + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRemovedContextualViewer(); + + public static final String KEY = "contextual-viewer"; + + @Override + public String key() { + return ServiceRemovedContextualViewer.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Removed ctx) { + final ContextRender currentContext = ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + final MetadataAccess metadata = viewer.metadata(); + final ContextRender oldOrNewContext = metadata + .getOrThrow(MetadataKeyHolder.CONTEXTUAL_VIEWER) + .context(); + if (currentContext.id().equals(oldOrNewContext.id())) { + metadata.remove(MetadataKeyHolder.CONTEXTUAL_VIEWER); + } + } + } + + private ServiceRemovedContextualViewer() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedLogging.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedLogging.java new file mode 100644 index 0000000..f1a2cbb --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedLogging.java @@ -0,0 +1,34 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRemovedLogging + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRemovedLogging(); + + public static final String KEY = "logging"; + + @Override + public String key() { + return ServiceRemovedLogging.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Removed ctx) { + final ContextRender context = ctx.context(); + for (final Viewer viewer : ctx.viewers()) { + context + .manager() + .logger() + .debug("Viewer '%s' removed from view '%s'.", viewer, context.view().instance()); + } + } + + private ServiceRemovedLogging() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedOnViewerRemoved.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedOnViewerRemoved.java new file mode 100644 index 0000000..b9c6195 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedOnViewerRemoved.java @@ -0,0 +1,37 @@ +package net.infumia.frame.pipeline.service.viewer; + +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import net.infumia.frame.view.ViewHandler; +import net.infumia.frame.viewer.Viewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRemovedOnViewerRemoved + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRemovedOnViewerRemoved(); + + public static final String KEY = "on-viewer-removed"; + + @Override + public String key() { + return ServiceRemovedOnViewerRemoved.KEY; + } + + @Override + public void accept(@NotNull final PipelineContextViewer.Removed ctx) { + final Object instance = ctx.context().view().instance(); + if (instance instanceof ViewHandler) { + final ViewHandler handler = (ViewHandler) instance; + for (final Viewer viewer : ctx.viewers()) { + handler.onViewerRemoved( + viewer.metadata().getOrThrow(MetadataKeyHolder.CONTEXTUAL_VIEWER) + ); + } + } + } + + private ServiceRemovedOnViewerRemoved() {} +} diff --git a/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedStopUpdateTask.java b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedStopUpdateTask.java new file mode 100644 index 0000000..2db76a1 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/pipeline/service/viewer/ServiceRemovedStopUpdateTask.java @@ -0,0 +1,33 @@ +package net.infumia.frame.pipeline.service.viewer; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextViewer; +import org.jetbrains.annotations.NotNull; + +public final class ServiceRemovedStopUpdateTask + implements PipelineServiceConsumer { + + public static final PipelineServiceConsumer INSTANCE = + new ServiceRemovedStopUpdateTask(); + + public static final String KEY = "stop-update-task"; + + @Override + public String key() { + return ServiceRemovedStopUpdateTask.KEY; + } + + @NotNull + @Override + public CompletableFuture handle(@NotNull final PipelineContextViewer.Removed ctx) { + final ContextRender context = ctx.context(); + if (context.viewers().isEmpty()) { + return context.pipelines().executeStopUpdate(); + } + return CompletableFuture.completedFuture(State.CONTINUE); + } + + private ServiceRemovedStopUpdateTask() {} +} diff --git a/core/src/main/java/net/infumia/frame/slot/LayoutSlotImpl.java b/core/src/main/java/net/infumia/frame/slot/LayoutSlotImpl.java new file mode 100644 index 0000000..a540fec --- /dev/null +++ b/core/src/main/java/net/infumia/frame/slot/LayoutSlotImpl.java @@ -0,0 +1,45 @@ +package net.infumia.frame.slot; + +import java.util.Arrays; +import java.util.function.IntFunction; +import net.infumia.frame.element.ElementItemBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class LayoutSlotImpl implements LayoutSlot { + + private final char character; + private final int@NotNull[] slots; + private IntFunction builderFactory; + + public LayoutSlotImpl(final char character, final int@NotNull[] slots) { + this.character = character; + this.slots = slots; + } + + @Override + public char character() { + return this.character; + } + + @Override + public int@NotNull[] slots() { + return this.slots; + } + + @Nullable + @Override + public IntFunction builderFactory() { + return this.builderFactory; + } + + @Override + public void builderFactory(@Nullable final IntFunction builderFactory) { + this.builderFactory = builderFactory; + } + + @Override + public boolean contains(final int slot) { + return Arrays.stream(this.slots).anyMatch(i -> i == slot); + } +} diff --git a/core/src/main/java/net/infumia/frame/slot/SlotFinder.java b/core/src/main/java/net/infumia/frame/slot/SlotFinder.java new file mode 100644 index 0000000..acebc86 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/slot/SlotFinder.java @@ -0,0 +1,82 @@ +package net.infumia.frame.slot; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.BiFunction; +import net.infumia.frame.InvTypeRich; +import net.infumia.frame.SlotConverter; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.element.ElementItemBuilder; +import net.infumia.frame.element.ElementItemBuilderRich; +import net.infumia.frame.util.Preconditions; +import net.infumia.frame.view.ViewContainerRich; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SlotFinder { + + private final List> availableSlotFinders = + new ArrayList<>(); + private final Collection nonRenderedBuilders = new ArrayList<>(); + private final ContextRender context; + + public SlotFinder(@NotNull final ContextRender context) { + this.context = context; + } + + @Nullable + public LayoutSlot findLayoutSlot(final char character) { + return this.context.layouts().get(character); + } + + public int findFirstSlot() { + return this.context.container().firstSlot(); + } + + public int findLastSlot() { + return this.context.container().lastSlot(); + } + + public int findResultSlot() { + final InvTypeRich type = ((ViewContainerRich) this.context.container()).typeRich(); + final int[] slots = type.resultSlots(); + Preconditions.state( + slots.length != 0, + "No result slot available for type '%s'", + type.type() + ); + Preconditions.state(slots.length == 1, "Multiple result slots not supported right now!"); + return slots[0]; + } + + public int toSlot(final int row, final int column) { + return SlotConverter.convertSlot( + row, + column, + this.context.container().rowsCount(), + this.context.container().columnsCount() + ); + } + + @NotNull + public List> availableSlotFinders() { + return Collections.unmodifiableList(this.availableSlotFinders); + } + + @NotNull + public Collection nonRenderedBuilders() { + return this.nonRenderedBuilders; + } + + public void addAvailableSlotFinder( + @NotNull final BiFunction indexAndSlotToBuilder + ) { + this.availableSlotFinders.add(indexAndSlotToBuilder); + } + + public void addNonRenderedBuilder(@NotNull final ElementItemBuilderRich builder) { + this.nonRenderedBuilders.add(builder); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateFactoryImpl.java b/core/src/main/java/net/infumia/frame/state/StateFactoryImpl.java new file mode 100644 index 0000000..8978fe6 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateFactoryImpl.java @@ -0,0 +1,338 @@ +package net.infumia.frame.state; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.function.Supplier; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.element.pagination.ElementPaginationBuilder; +import net.infumia.frame.element.pagination.ElementPaginationBuilderImpl; +import net.infumia.frame.element.pagination.ElementPaginationBuilderRich; +import net.infumia.frame.element.pagination.SourceProvider; +import net.infumia.frame.state.pagination.ElementConfigurer; +import net.infumia.frame.state.pagination.StatePagination; +import net.infumia.frame.state.value.StateValueComputed; +import net.infumia.frame.state.value.StateValueImmutable; +import net.infumia.frame.state.value.StateValueInitial; +import net.infumia.frame.state.value.StateValueMutable; +import net.infumia.frame.typedkey.TypedKey; +import net.infumia.frame.util.Lazy; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateFactoryImpl implements StateFactory { + + private static final AtomicLong COUNTER = new AtomicLong(); + + private final StateRegistry registry; + + public StateFactoryImpl(@NotNull final StateRegistry registry) { + this.registry = registry; + } + + @NotNull + @Override + public StateInitial createInitialState(@NotNull final TypedKey stateKey) { + return this.registered( + new StateInitialImpl<>( + StateFactoryImpl.nextStateId(), + (host, __) -> new StateValueInitial<>(host, stateKey.key()), + stateKey.key() + ) + ); + } + + @NotNull + @Override + public State createState(@NotNull final T initialValue) { + return this.registered( + new StateImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueImmutable<>(initialValue) + ) + ); + } + + @NotNull + @Override + public StateMutable createMutableState(@Nullable final T initialValue) { + return this.registered( + new StateMutableImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueMutable<>(initialValue) + ) + ); + } + + @NotNull + @Override + public State createComputedState(@NotNull final Function computation) { + return this.registered( + new StateImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueComputed<>(() -> computation.apply(host)) + ) + ); + } + + @NotNull + @Override + public State createComputedState(@NotNull final Supplier computation) { + return this.registered( + new StateImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueComputed<>(computation) + ) + ); + } + + @NotNull + @Override + public State createLazyState(@NotNull final Function computation) { + return this.registered( + new StateImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueComputed<>(Lazy.of(() -> computation.apply(host))) + ) + ); + } + + @NotNull + @Override + public State createLazyState(@NotNull final Supplier computation) { + return this.registered( + new StateImpl<>(StateFactoryImpl.nextStateId(), (host, __) -> + new StateValueComputed<>(Lazy.of(computation)) + ) + ); + } + + @NotNull + @Override + public StatePagination createPaginationState( + @NotNull final List source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildPaginationState(source).elementConfigurer(configurer).buildPagination(); + } + + @NotNull + @Override + public StatePagination createComputedPaginationState( + @NotNull final Supplier> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildComputedPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createComputedPaginationState( + @NotNull final Function> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildComputedPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createComputedAsyncPaginationState( + @NotNull final Supplier>> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildComputedAsyncPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createComputedAsyncPaginationState( + @NotNull final Function>> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildComputedAsyncPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createLazyPaginationState( + @NotNull final Supplier> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildLazyPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createLazyPaginationState( + @NotNull final Function> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildLazyPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createLazyAsyncPaginationState( + @NotNull final Supplier>> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildLazyAsyncPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public StatePagination createLazyAsyncPaginationState( + @NotNull final Function>> source, + @NotNull final ElementConfigurer configurer + ) { + return this.buildLazyAsyncPaginationState(source) + .elementConfigurer(configurer) + .buildPagination(); + } + + @NotNull + @Override + public ElementPaginationBuilder buildPaginationState(@NotNull final List source) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Immutable<>(source), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildComputedPaginationState( + @NotNull final Supplier> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>( + () -> CompletableFuture.completedFuture(source.get()), + true, + false + ), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildComputedPaginationState( + @NotNull final Function> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>( + context -> CompletableFuture.completedFuture(source.apply(context)), + true, + false + ), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildComputedAsyncPaginationState( + @NotNull final Supplier>> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>(source, true, false), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildComputedAsyncPaginationState( + @NotNull final Function>> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>(source, true, false), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildLazyPaginationState( + @NotNull final Supplier> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>( + () -> CompletableFuture.completedFuture(source.get()), + false, + true + ), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildLazyPaginationState( + @NotNull final Function> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>( + context -> CompletableFuture.completedFuture(source.apply(context)), + false, + true + ), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildLazyAsyncPaginationState( + @NotNull final Supplier>> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>(source, false, true), + this::createPaginationState + ); + } + + @NotNull + @Override + public ElementPaginationBuilder buildLazyAsyncPaginationState( + @NotNull final Function>> source + ) { + return new ElementPaginationBuilderImpl<>( + new SourceProvider.Computed<>(source, false, true), + this::createPaginationState + ); + } + + @NotNull + private StatePagination createPaginationState( + @NotNull final ElementPaginationBuilder builder + ) { + return this.registered( + new StatePaginationImpl(StateFactoryImpl.nextStateId(), (host, state) -> + new StateValueImmutable<>( + ((ElementPaginationBuilderRich) builder).associated(state).build(host) + ) + ) + ); + } + + @NotNull + private > S registered(@NotNull final S state) { + this.registry.register(state); + return state; + } + + private static long nextStateId() { + return StateFactoryImpl.COUNTER.getAndIncrement(); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateImpl.java b/core/src/main/java/net/infumia/frame/state/StateImpl.java new file mode 100644 index 0000000..ef44464 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateImpl.java @@ -0,0 +1,115 @@ +package net.infumia.frame.state; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.state.value.StateValue; +import net.infumia.frame.state.value.StateValueFactory; +import net.infumia.frame.state.value.StateValueHostHolder; +import net.infumia.frame.state.value.StateValueHostRich; +import net.infumia.frame.state.watcher.StateWatcherAccess; +import net.infumia.frame.state.watcher.StateWatcherUpdate; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StateImpl implements StateRich { + + private final long id; + private final StateValueFactory valueFactory; + + public StateImpl(final long id, @NotNull final StateValueFactory valueFactory) { + this.id = id; + this.valueFactory = valueFactory; + } + + @Override + public long id() { + return this.id; + } + + @NotNull + @Override + public StateValueFactory valueFactory() { + return this.valueFactory; + } + + @Nullable + @Override + public StateValue manualUpdate(@NotNull final StateValueHostHolder host) { + return ((StateValueHostRich) host.stateValueHost()).updateStateValue(this); + } + + @NotNull + @Override + public CompletableFuture> manualUpdateWait( + @NotNull final StateValueHostHolder host + ) { + return ((StateValueHostRich) host.stateValueHost()).updateStateValueWait(this); + } + + @Nullable + @Override + public T get(@NotNull final StateValueHostHolder host) { + return ((StateValueHostRich) host.stateValueHost()).accessStateValueOrInitialize( + this + ).value(); + } + + @NotNull + @Override + public T getOtThrow(@NotNull final StateValueHostHolder host) { + return Preconditions.stateNotNull( + this.get(host), + "Value for state '%s' not found!", + this.id + ); + } + + @NotNull + @Override + public CompletableFuture<@Nullable T> getWait(@NotNull final StateValueHostHolder host) { + return ((StateValueHostRich) host.stateValueHost()).accessStateValueOrInitializeWait( + this + ).thenApply(StateValue::value); + } + + @NotNull + @Override + public CompletableFuture getOtThrowWait(@NotNull final StateValueHostHolder host) { + return this.getWait(host).thenApply(value -> + Preconditions.stateNotNull(value, "Value for state '%s' not found!", this.id) + ); + } + + @Override + public void watchAccess( + @NotNull final StateValueHostHolder host, + @NotNull final StateWatcherAccess watcher + ) { + ((StateValueHostRich) host.stateValueHost()).watchStateAccess(this, watcher); + } + + @Override + public void watchUpdate( + @NotNull final StateValueHostHolder host, + @NotNull final StateWatcherUpdate watcher + ) { + ((StateValueHostRich) host.stateValueHost()).watchStateUpdate(this, watcher); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.id); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof StateRich)) { + return false; + } + return this.id == ((StateRich) o).id(); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateInitialImpl.java b/core/src/main/java/net/infumia/frame/state/StateInitialImpl.java new file mode 100644 index 0000000..97581d9 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateInitialImpl.java @@ -0,0 +1,23 @@ +package net.infumia.frame.state; + +import net.infumia.frame.state.value.StateValueFactory; +import org.jetbrains.annotations.NotNull; + +public final class StateInitialImpl extends StateImpl implements StateInitialRich { + + private final String key; + + public StateInitialImpl( + final long id, + @NotNull final StateValueFactory valueFactory, + @NotNull final String key + ) { + super(id, valueFactory); + this.key = key; + } + + @Override + public String key() { + return this.key; + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateInitialRich.java b/core/src/main/java/net/infumia/frame/state/StateInitialRich.java new file mode 100644 index 0000000..0dcbea2 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateInitialRich.java @@ -0,0 +1,3 @@ +package net.infumia.frame.state; + +public interface StateInitialRich extends StateRich, StateInitial {} diff --git a/core/src/main/java/net/infumia/frame/state/StateMutableImpl.java b/core/src/main/java/net/infumia/frame/state/StateMutableImpl.java new file mode 100644 index 0000000..1d425ea --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateMutableImpl.java @@ -0,0 +1,31 @@ +package net.infumia.frame.state; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.state.value.StateValue; +import net.infumia.frame.state.value.StateValueFactory; +import net.infumia.frame.state.value.StateValueHostHolder; +import net.infumia.frame.state.value.StateValueHostRich; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateMutableImpl extends StateImpl implements StateMutableRich { + + public StateMutableImpl(final long id, @NotNull final StateValueFactory valueFactory) { + super(id, valueFactory); + } + + @Nullable + @Override + public StateValue set(@NotNull final StateValueHostHolder host, @Nullable final T value) { + return ((StateValueHostRich) host.stateValueHost()).updateStateValue(this, value); + } + + @NotNull + @Override + public CompletableFuture<@Nullable StateValue> setWait( + @NotNull final StateValueHostHolder host, + @Nullable final T value + ) { + return ((StateValueHostRich) host.stateValueHost()).updateStateValueWait(this, value); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateMutableRich.java b/core/src/main/java/net/infumia/frame/state/StateMutableRich.java new file mode 100644 index 0000000..ad7ce15 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateMutableRich.java @@ -0,0 +1,3 @@ +package net.infumia.frame.state; + +public interface StateMutableRich extends StateRich, StateMutable {} diff --git a/core/src/main/java/net/infumia/frame/state/StatePaginationImpl.java b/core/src/main/java/net/infumia/frame/state/StatePaginationImpl.java new file mode 100644 index 0000000..2e558fa --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StatePaginationImpl.java @@ -0,0 +1,17 @@ +package net.infumia.frame.state; + +import net.infumia.frame.element.pagination.ElementPagination; +import net.infumia.frame.state.value.StateValueFactory; +import org.jetbrains.annotations.NotNull; + +public final class StatePaginationImpl + extends StateImpl + implements StatePaginationRich { + + public StatePaginationImpl( + final long id, + @NotNull final StateValueFactory valueFactory + ) { + super(id, valueFactory); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StatePaginationRich.java b/core/src/main/java/net/infumia/frame/state/StatePaginationRich.java new file mode 100644 index 0000000..2c56028 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StatePaginationRich.java @@ -0,0 +1,6 @@ +package net.infumia.frame.state; + +import net.infumia.frame.element.pagination.ElementPagination; +import net.infumia.frame.state.pagination.StatePagination; + +public interface StatePaginationRich extends StateRich, StatePagination {} diff --git a/core/src/main/java/net/infumia/frame/state/StateRegistry.java b/core/src/main/java/net/infumia/frame/state/StateRegistry.java new file mode 100644 index 0000000..5bb1265 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateRegistry.java @@ -0,0 +1,64 @@ +package net.infumia.frame.state; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import net.infumia.frame.logger.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateRegistry implements Iterable> { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Map> states = new HashMap<>(); + private final Logger logger; + + public StateRegistry(@NotNull final Logger logger) { + this.logger = logger; + } + + @Nullable + public StateRich byId(final long internalId) { + try { + this.lock.readLock().lock(); + return this.states.get(internalId); + } finally { + this.lock.readLock().unlock(); + } + } + + @SuppressWarnings("unchecked") + public void register(@NotNull final StateRich state) { + try { + this.lock.writeLock().lock(); + this.states.put(state.id(), (StateRich) state); + this.logger.debug("State '%s:%s' registered", state.id(), state); + } finally { + this.lock.writeLock().unlock(); + } + } + + public void unregister(@NotNull final StateRich state) { + try { + this.lock.writeLock().lock(); + this.states.remove(state.id()); + this.logger.debug("State '%s:%s' unregistered", state.id(), state); + } finally { + this.lock.writeLock().unlock(); + } + } + + @NotNull + @Override + public Iterator> iterator() { + try { + this.lock.readLock().lock(); + return Collections.unmodifiableCollection(this.states.values()).iterator(); + } finally { + this.lock.readLock().unlock(); + } + } +} diff --git a/core/src/main/java/net/infumia/frame/state/StateRich.java b/core/src/main/java/net/infumia/frame/state/StateRich.java new file mode 100644 index 0000000..ad75ecc --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/StateRich.java @@ -0,0 +1,21 @@ +package net.infumia.frame.state; + +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.state.value.StateValue; +import net.infumia.frame.state.value.StateValueFactory; +import net.infumia.frame.state.value.StateValueHostHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface StateRich extends State { + long id(); + + @NotNull + StateValueFactory valueFactory(); + + @Nullable + StateValue manualUpdate(@NotNull StateValueHostHolder host); + + @NotNull + CompletableFuture> manualUpdateWait(@NotNull StateValueHostHolder host); +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueComputed.java b/core/src/main/java/net/infumia/frame/state/value/StateValueComputed.java new file mode 100644 index 0000000..d26f594 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueComputed.java @@ -0,0 +1,30 @@ +package net.infumia.frame.state.value; + +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateValueComputed implements StateValue { + + private final Supplier computation; + + public StateValueComputed(@NotNull final Supplier computation) { + this.computation = computation; + } + + @Nullable + @Override + public T value() { + return this.computation.get(); + } + + @Override + public void value(@Nullable final T value) { + throw new UnsupportedOperationException("Immutable state!"); + } + + @Override + public boolean mutable() { + return false; + } +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueFactory.java b/core/src/main/java/net/infumia/frame/state/value/StateValueFactory.java new file mode 100644 index 0000000..0d4c4af --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueFactory.java @@ -0,0 +1,8 @@ +package net.infumia.frame.state.value; + +import java.util.function.BiFunction; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.state.State; + +@FunctionalInterface +public interface StateValueFactory extends BiFunction, StateValue> {} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueHostImpl.java b/core/src/main/java/net/infumia/frame/state/value/StateValueHostImpl.java new file mode 100644 index 0000000..5168500 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueHostImpl.java @@ -0,0 +1,236 @@ +package net.infumia.frame.state.value; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.pipeline.PipelineServiceConsumer; +import net.infumia.frame.pipeline.context.PipelineContextState; +import net.infumia.frame.pipeline.executor.PipelineExecutorState; +import net.infumia.frame.pipeline.executor.PipelineExecutorStateImpl; +import net.infumia.frame.service.Implementation; +import net.infumia.frame.state.StateMutableRich; +import net.infumia.frame.state.StateRich; +import net.infumia.frame.state.watcher.StateWatcherAccess; +import net.infumia.frame.state.watcher.StateWatcherUpdate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateValueHostImpl implements StateValueHostRich { + + private final Map, StateValue> values = new HashMap<>(); + private final ContextBase context; + private final PipelineExecutorState pipelines; + + public StateValueHostImpl(@NotNull final ContextBase context) { + this.context = context; + this.pipelines = new PipelineExecutorStateImpl(context); + } + + @NotNull + @Override + public PipelineExecutorState statePipelines() { + return this.pipelines; + } + + @NotNull + @Override + public Map, StateValue> stateValues() { + return Collections.unmodifiableMap(this.values); + } + + @NotNull + @Override + public StateValue accessStateValueOrInitialize(@NotNull final StateRich state) { + return this.accessStateValueOrInitializeInternally(state, this.accessStateValue(state)); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public StateValue accessStateValue(@NotNull final StateRich state) { + final StateValue value = this.values.get(state); + if (value == null) { + this.context.manager() + .logger() + .debug("State '%s' not found in '%s'!", state, this.values); + return null; + } + this.pipelines.executeAccess(state, value); + return (StateValue) value; + } + + @Nullable + @Override + public StateValue updateStateValue( + @NotNull final StateMutableRich state, + @Nullable final T value + ) { + final StateValue stateValue = this.accessStateValue(state); + if (stateValue == null) { + this.context.manager().logger().debug("State '%s' is not registered!", state); + return null; + } + final T oldValue = stateValue.value(); + stateValue.value(value); + this.pipelines.executeUpdate(state, oldValue, stateValue); + return stateValue; + } + + @Nullable + @Override + public StateValue updateStateValue(@NotNull final StateRich state) { + final StateValue stateValue = this.accessStateValue(state); + if (stateValue == null) { + this.context.manager().logger().debug("State '%s' is not registered!", state); + return null; + } + this.pipelines.executeUpdate(state, stateValue.value(), stateValue); + return stateValue; + } + + @NotNull + @Override + public CompletableFuture> accessStateValueOrInitializeWait( + @NotNull final StateRich state + ) { + return this.accessStateValueWait(state).thenApply(value -> + this.accessStateValueOrInitializeInternally(state, value) + ); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public CompletableFuture<@Nullable StateValue> accessStateValueWait( + @NotNull final StateRich state + ) { + final StateValue value = this.values.get(state); + if (value == null) { + this.context.manager() + .logger() + .debug("State '%s' not found in '%s'!", state, this.values); + return CompletableFuture.completedFuture(null); + } + return this.pipelines.executeAccess(state, value).thenApply(__ -> (StateValue) value); + } + + @NotNull + @Override + public CompletableFuture<@Nullable StateValue> updateStateValueWait( + @NotNull final StateMutableRich state, + @Nullable final T value + ) { + return this.accessStateValueWait(state).thenCompose(stateValue -> { + if (stateValue == null) { + this.context.manager().logger().debug("State '%s' is not registered!", state); + return CompletableFuture.completedFuture(null); + } + final T oldValue = stateValue.value(); + stateValue.value(value); + return this.pipelines.executeUpdate(state, oldValue, stateValue).thenApply(__ -> + stateValue + ); + }); + } + + @NotNull + @Override + public CompletableFuture<@Nullable StateValue> updateStateValueWait( + @NotNull final StateRich state + ) { + return this.accessStateValueWait(state).thenCompose(stateValue -> { + if (stateValue == null) { + this.context.manager().logger().debug("State '%s' is not registered!", state); + return CompletableFuture.completedFuture(null); + } + return this.pipelines.executeUpdate( + state, + stateValue.value(), + stateValue + ).thenApply(__ -> stateValue); + }); + } + + @Override + public void watchStateAccess( + @NotNull final StateRich state, + @NotNull final StateWatcherAccess watcher + ) { + this.pipelines.applyAccess( + Implementation.register( + new PipelineServiceConsumer() { + @Override + public String key() { + return ""; + } + + @Override + @SuppressWarnings("unchecked") + public void accept(@NotNull final PipelineContextState.Access ctx) { + if (((StateRich) ctx.state()).id() == state.id()) { + watcher.access((StateValue) ctx.value()); + } + } + } + ) + ); + } + + @Override + public void watchStateUpdate( + @NotNull final StateRich state, + @NotNull final StateWatcherUpdate watcher + ) { + this.pipelines.applyUpdate( + Implementation.register( + new PipelineServiceConsumer() { + @Override + public String key() { + return ""; + } + + @Override + @SuppressWarnings("unchecked") + public void accept(@NotNull final PipelineContextState.Update ctx) { + if (((StateRich) ctx.state()).id() == state.id()) { + watcher.update( + new StateUpdate<>( + state, + (T) ctx.oldValue(), + (StateValue) ctx.value() + ) + ); + } + } + } + ) + ); + } + + @Override + @SuppressWarnings("unchecked") + public void initializeState( + @NotNull final StateRich state, + @NotNull final StateValue value + ) { + this.values.put((StateRich) state, (StateValue) value); + this.context.manager() + .logger() + .debug("State '%s' initialized with value '%s'", state, value); + } + + @NotNull + private StateValue accessStateValueOrInitializeInternally( + @NotNull final StateRich state, + @Nullable final StateValue value + ) { + if (value != null) { + return value; + } + final StateValue stateValue = state.valueFactory().apply(this.context, state); + this.initializeState(state, stateValue); + return stateValue; + } +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueHostRich.java b/core/src/main/java/net/infumia/frame/state/value/StateValueHostRich.java new file mode 100644 index 0000000..a579199 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueHostRich.java @@ -0,0 +1,58 @@ +package net.infumia.frame.state.value; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.pipeline.executor.PipelineExecutorState; +import net.infumia.frame.state.StateMutableRich; +import net.infumia.frame.state.StateRich; +import net.infumia.frame.state.watcher.StateWatcherAccess; +import net.infumia.frame.state.watcher.StateWatcherUpdate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface StateValueHostRich extends StateValueHost { + @NotNull + PipelineExecutorState statePipelines(); + + @NotNull + Map, StateValue> stateValues(); + + @NotNull + StateValue accessStateValueOrInitialize(@NotNull StateRich state); + + @Nullable + StateValue accessStateValue(@NotNull StateRich state); + + @Nullable + StateValue updateStateValue(@NotNull StateMutableRich state, @Nullable T value); + + @Nullable + StateValue updateStateValue(@NotNull StateRich state); + + @NotNull + CompletableFuture> accessStateValueOrInitializeWait( + @NotNull StateRich state + ); + + @NotNull + CompletableFuture<@Nullable StateValue> accessStateValueWait( + @NotNull StateRich state + ); + + @NotNull + CompletableFuture<@Nullable StateValue> updateStateValueWait( + @NotNull StateMutableRich state, + @Nullable T value + ); + + @NotNull + CompletableFuture<@Nullable StateValue> updateStateValueWait( + @NotNull StateRich state + ); + + void watchStateAccess(@NotNull StateRich state, @NotNull StateWatcherAccess watcher); + + void watchStateUpdate(@NotNull StateRich state, @NotNull StateWatcherUpdate watcher); + + void initializeState(@NotNull StateRich state, @NotNull StateValue value); +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueImmutable.java b/core/src/main/java/net/infumia/frame/state/value/StateValueImmutable.java new file mode 100644 index 0000000..b016c4b --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueImmutable.java @@ -0,0 +1,29 @@ +package net.infumia.frame.state.value; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateValueImmutable implements StateValue { + + private final T value; + + public StateValueImmutable(@NotNull final T value) { + this.value = value; + } + + @NotNull + @Override + public T value() { + return this.value; + } + + @Override + public void value(@Nullable final T value) { + throw new UnsupportedOperationException("Immutable state!"); + } + + @Override + public boolean mutable() { + return false; + } +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueInitial.java b/core/src/main/java/net/infumia/frame/state/value/StateValueInitial.java new file mode 100644 index 0000000..b8cf61b --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueInitial.java @@ -0,0 +1,71 @@ +package net.infumia.frame.state.value; + +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.util.Lazy; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class StateValueInitial implements StateValue { + + private final String stateKey; + private final ContextBase context; + private StateValue backingValue; + + public StateValueInitial(@NotNull final ContextBase context, @NotNull final String stateKey) { + this.stateKey = stateKey; + this.context = context; + this.backingValue = this.createBackingValue(stateKey); + } + + @Nullable + @Override + public T value() { + return this.backingValue.value(); + } + + @Override + public void value(@Nullable final T value) { + this.backingValue.value(value); + } + + @Override + public boolean mutable() { + return this.backingValue.mutable(); + } + + public void reset() { + this.backingValue = this.createBackingValue(this.stateKey); + } + + @SuppressWarnings("unchecked") + private StateValue createBackingValue(@NotNull final String key) { + return new StateValueComputed<>( + Lazy.of(() -> { + final TypedKeyStorageImmutable initialData = Preconditions.stateNotNull( + this.context.initialData(), + "Initial data not found even tough there is a initial state '%s' registered!", + key + ); + final Object value = Preconditions.stateNotNull( + initialData.get(key), + "No initial data found for state '%s'", + key + ); + try { + return (T) value; + } catch (final ClassCastException e) { + throw new RuntimeException( + String.format( + "Invalid data type '%s' for state '%s'!", + value.getClass().getSimpleName(), + key + ), + e + ); + } + }) + ); + } +} diff --git a/core/src/main/java/net/infumia/frame/state/value/StateValueMutable.java b/core/src/main/java/net/infumia/frame/state/value/StateValueMutable.java new file mode 100644 index 0000000..22a1134 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/state/value/StateValueMutable.java @@ -0,0 +1,28 @@ +package net.infumia.frame.state.value; + +import org.jetbrains.annotations.Nullable; + +public final class StateValueMutable implements StateValue { + + private T value; + + public StateValueMutable(@Nullable final T value) { + this.value = value; + } + + @Override + @Nullable + public T value() { + return this.value; + } + + @Override + public void value(@Nullable final T value) { + this.value = value; + } + + @Override + public boolean mutable() { + return true; + } +} diff --git a/core/src/main/java/net/infumia/frame/task/TaskFactoryImpl.java b/core/src/main/java/net/infumia/frame/task/TaskFactoryImpl.java new file mode 100644 index 0000000..3b304fb --- /dev/null +++ b/core/src/main/java/net/infumia/frame/task/TaskFactoryImpl.java @@ -0,0 +1,60 @@ +package net.infumia.frame.task; + +import java.io.Closeable; +import java.time.Duration; +import net.infumia.frame.logger.Logger; +import net.infumia.frame.util.RunnableThrowable; +import net.infumia.frame.util.Ticks; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +// TODO: portlek, Add paper's folia support. +public final class TaskFactoryImpl implements TaskFactory { + + private final Plugin plugin; + private final Logger logger; + + public TaskFactoryImpl(@NotNull final Plugin plugin, @NotNull final Logger logger) { + this.plugin = plugin; + this.logger = logger; + } + + @NotNull + @Override + public Closeable sync(@NotNull final RunnableThrowable task) { + final BukkitTask bukkitTask = Bukkit.getScheduler() + .runTask(this.plugin, () -> { + try { + task.run(); + } catch (final Throwable e) { + this.logger.error(e, "An error occurred while running a sync task."); + } + }); + return bukkitTask::cancel; + } + + @NotNull + @Override + public Closeable sync( + @NotNull final RunnableThrowable task, + @NotNull final Duration delay, + @NotNull final Duration period + ) { + final BukkitTask bukkitTask = Bukkit.getScheduler() + .runTaskTimer( + this.plugin, + () -> { + try { + task.run(); + } catch (final Throwable e) { + this.logger.error(e, "An error occurred while running a sync task."); + } + }, + Ticks.toTicks(delay), + Ticks.toTicks(period) + ); + return bukkitTask::cancel; + } +} diff --git a/core/src/main/java/net/infumia/frame/view/ViewContainerImpl.java b/core/src/main/java/net/infumia/frame/view/ViewContainerImpl.java new file mode 100644 index 0000000..b30b0db --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/ViewContainerImpl.java @@ -0,0 +1,108 @@ +package net.infumia.frame.view; + +import net.infumia.frame.InvTypeRich; +import net.infumia.frame.InvTypes; +import net.infumia.frame.type.InvType; +import net.infumia.frame.viewer.Viewer; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ViewContainerImpl implements ViewContainerRich { + + private final Inventory inventory; + private final InvTypeRich type; + + public ViewContainerImpl(@NotNull final Inventory inventory, @NotNull final InvTypeRich type) { + this.inventory = inventory; + this.type = type; + } + + @NotNull + @Override + public Inventory inventory() { + return this.inventory; + } + + @NotNull + @Override + public InvType type() { + return this.type.type(); + } + + @Override + public int size() { + return this.inventory.getSize(); + } + + @Override + public int slotsCount() { + return this.size() - 1; + } + + @Override + public int rowsCount() { + return this.size() / this.columnsCount(); + } + + @Override + public int columnsCount() { + return this.type.columns(); + } + + @Override + public int firstSlot() { + return this.type == InvTypes.PLAYER ? 45 : 0; + } + + @Override + public int lastSlot() { + int lastSlot = this.slotsCount(); + final int[] resultSlots = this.type.resultSlots(); + for (final int resultSlot : resultSlots) { + if (resultSlot == lastSlot) { + lastSlot--; + } + } + return lastSlot; + } + + @Override + public boolean hasItem(final int slot) { + return this.inventory.getItem(slot) != null; + } + + @Override + public void removeItem(final int slot) { + this.inventory.setItem(slot, null); + } + + @Override + public void addItem(final int slot, @NotNull final ItemStack item) { + this.inventory.setItem(slot, item); + } + + @Override + public void open(@NotNull final Viewer viewer) { + viewer.open(this); + } + + @Override + public boolean isPlayerInventory() { + return this.inventory instanceof PlayerInventory; + } + + @Nullable + @Override + public ViewContainer at(final int slot) { + return slot >= this.firstSlot() && slot <= this.lastSlot() ? this : null; + } + + @NotNull + @Override + public InvTypeRich typeRich() { + return this.type; + } +} diff --git a/core/src/main/java/net/infumia/frame/view/ViewContainerRich.java b/core/src/main/java/net/infumia/frame/view/ViewContainerRich.java new file mode 100644 index 0000000..e4bdba6 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/ViewContainerRich.java @@ -0,0 +1,9 @@ +package net.infumia.frame.view; + +import net.infumia.frame.InvTypeRich; +import org.jetbrains.annotations.NotNull; + +public interface ViewContainerRich extends ViewContainer { + @NotNull + InvTypeRich typeRich(); +} diff --git a/core/src/main/java/net/infumia/frame/view/ViewCreatorImpl.java b/core/src/main/java/net/infumia/frame/view/ViewCreatorImpl.java new file mode 100644 index 0000000..8cd3008 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/ViewCreatorImpl.java @@ -0,0 +1,82 @@ +package net.infumia.frame.view; + +import java.lang.reflect.Constructor; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.injection.AnnotationAccessor; +import net.infumia.frame.injection.InjectionRequester; +import net.infumia.frame.injection.InjectionService; +import net.infumia.frame.injection.InjectionServicePipeline; +import net.infumia.frame.injector.InjectionRequest; +import net.infumia.frame.injector.Injector; +import net.infumia.frame.injector.InjectorRegistry; +import net.infumia.frame.util.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ViewCreatorImpl implements ViewCreator { + + private final InjectorRegistry injectors; + private final InjectionServicePipeline pipeline; + private final InjectionRequester requester; + + public ViewCreatorImpl() { + this.injectors = InjectorRegistry.create().register(new NoArg()); + this.pipeline = InjectionServicePipeline.create(InjectionService.create(this.injectors)); + this.requester = InjectionRequester.create(this.pipeline); + } + + @NotNull + @Override + public InjectionServicePipeline pipeline() { + return this.pipeline; + } + + @NotNull + @Override + public InjectorRegistry injectors() { + return this.injectors; + } + + @NotNull + @Override + public CompletableFuture create(@NotNull final Class viewClass) { + return this.requester.request( + viewClass, + viewClass, + AnnotationAccessor.of(viewClass) + ).handle((value, throwable) -> { + if (throwable != null) { + throw new IllegalArgumentException( + String.format("Could not create view instance of '%s'.", viewClass), + throwable + ); + } + return Preconditions.argumentNotNull( + value, + "Both error and result is null when requesting an injection for view class '%s'. Please report this bug to the inventory framework you are using.", + viewClass + ); + }); + } + + private static final class NoArg implements Injector { + + @Nullable + @Override + public Object inject(@NotNull final InjectionRequest ctx) { + final Class viewClass = ctx.injectedClass(); + Constructor constructor = null; + boolean old = false; + try { + constructor = viewClass.getConstructor(); + old = constructor.isAccessible(); + return constructor.newInstance(); + } catch (final Exception ignored) {} finally { + if (constructor != null) { + constructor.setAccessible(old); + } + } + return null; + } + } +} diff --git a/core/src/main/java/net/infumia/frame/view/ViewEventHandler.java b/core/src/main/java/net/infumia/frame/view/ViewEventHandler.java new file mode 100644 index 0000000..f975be3 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/ViewEventHandler.java @@ -0,0 +1,47 @@ +package net.infumia.frame.view; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.viewer.ContextualViewer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ViewEventHandler { + @NotNull + CompletableFuture simulateOnInit(); + + @NotNull + CompletableFuture<@Nullable ContextRender> simulateOpen( + @NotNull Collection viewers, + @NotNull TypedKeyStorageImmutable initialData + ); + + @NotNull + CompletableFuture simulateOpenActive( + @NotNull ContextRender activeContext, + @NotNull Collection viewers + ); + + @NotNull + CompletableFuture simulateClick( + @NotNull ContextualViewer viewer, + @NotNull InventoryClickEvent event + ); + + @NotNull + CompletableFuture simulateClose(@NotNull ContextualViewer viewer); + + void handleItemPickup(@NotNull ContextualViewer viewer, @NotNull PlayerPickupItemEvent event); + + void handleItemDrop(@NotNull ContextualViewer viewer, @NotNull PlayerDropItemEvent event); + + void handleInventoryDrag(@NotNull ContextualViewer viewer, @NotNull InventoryDragEvent event); +} diff --git a/core/src/main/java/net/infumia/frame/view/ViewImpl.java b/core/src/main/java/net/infumia/frame/view/ViewImpl.java new file mode 100644 index 0000000..386ebb4 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/ViewImpl.java @@ -0,0 +1,214 @@ +package net.infumia.frame.view; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import net.infumia.frame.config.ViewConfigRich; +import net.infumia.frame.context.ContextBase; +import net.infumia.frame.context.view.ContextInit; +import net.infumia.frame.context.view.ContextOpen; +import net.infumia.frame.context.view.ContextRender; +import net.infumia.frame.context.view.ContextRenderRich; +import net.infumia.frame.metadata.MetadataKeyHolder; +import net.infumia.frame.pipeline.executor.PipelineExecutorView; +import net.infumia.frame.pipeline.executor.PipelineExecutorViewImpl; +import net.infumia.frame.service.ConsumerService; +import net.infumia.frame.slot.LayoutSlot; +import net.infumia.frame.typedkey.TypedKeyStorageImmutable; +import net.infumia.frame.util.Pair; +import net.infumia.frame.view.config.ViewConfig; +import net.infumia.frame.view.config.option.ViewConfigOptions; +import net.infumia.frame.viewer.ContextualViewer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ViewImpl implements View, ViewEventHandler { + + private final PipelineExecutorView pipelines = new PipelineExecutorViewImpl(this); + private final Object instance; + private final ContextInit context; + + public ViewImpl(@NotNull final ContextInit context, @NotNull final Object instance) { + this.context = context; + this.instance = instance; + } + + @NotNull + @Override + public ContextInit context() { + return this.context; + } + + @NotNull + @Override + public Object instance() { + return this.instance; + } + + @NotNull + @Override + public PipelineExecutorView pipelines() { + return this.pipelines; + } + + @NotNull + @Override + public CompletableFuture simulateOnInit() { + return this.pipelines.executeInit(this.context); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> simulateOpen( + @NotNull final Collection viewers, + @NotNull final TypedKeyStorageImmutable initialData + ) { + return this.pipelines.executeCreateViewers(viewers) + .thenCompose(v -> this.pipelines.executeCreateContext(v, initialData)) + .thenCompose(this::navigate); + } + + @NotNull + @Override + public CompletableFuture<@Nullable ContextRender> simulateOpenActive( + @NotNull final ContextRender activeContext, + @NotNull final Collection viewers + ) { + return this.pipelines.executeCreateViewers(viewers) + .thenCompose(((ContextRenderRich) activeContext)::simulateNavigate) + .thenApply(__ -> activeContext); + } + + @NotNull + @Override + public CompletableFuture simulateClick( + @NotNull final ContextualViewer viewer, + @NotNull final InventoryClickEvent event + ) { + return this.pipelines.executeClick(viewer, event); + } + + @NotNull + @Override + public CompletableFuture simulateClose( + @NotNull final ContextualViewer viewer + ) { + final ContextualViewer transitioning = viewer + .metadata() + .remove(MetadataKeyHolder.TRANSITIONING_FROM); + final Boolean forcedClose = viewer.metadata().remove(MetadataKeyHolder.FORCED_CLOSE); + final boolean forced = transitioning != null || (forcedClose != null && forcedClose); + return this.pipelines.executeClose(viewer, forced); + } + + @Override + public void handleItemPickup( + @NotNull final ContextualViewer viewer, + @NotNull final PlayerPickupItemEvent event + ) { + final ContextRender context = viewer.context(); + final ViewConfigRich config = (ViewConfigRich) context.config(); + config + .option(ViewConfigOptions.CANCEL_ON_PICKUP) + .filter(l -> l) + .ifPresent(cancel -> event.setCancelled(true)); + } + + @Override + public void handleItemDrop( + @NotNull final ContextualViewer viewer, + @NotNull final PlayerDropItemEvent event + ) { + final ContextRender context = viewer.context(); + final ViewConfigRich config = (ViewConfigRich) context.config(); + config + .option(ViewConfigOptions.CANCEL_ON_DROP) + .filter(l -> l) + .ifPresent(cancel -> event.setCancelled(true)); + } + + @Override + public void handleInventoryDrag( + @NotNull final ContextualViewer viewer, + @NotNull final InventoryDragEvent event + ) { + final ContextRender context = viewer.context(); + final ViewConfigRich config = (ViewConfigRich) context.config(); + config + .option(ViewConfigOptions.CANCEL_ON_DRAG) + .filter(l -> l) + .ifPresent(cancel -> event.setCancelled(true)); + } + + @NotNull + private CompletionStage<@Nullable ContextRender> navigate(@NotNull final ContextBase context) { + return this.pipelines.executeOpen(context) + .thenApply(Pair::second) + .thenCompose(open -> { + if (open.cancelled()) { + return CompletableFuture.completedFuture(null); + } else { + return this.executeProcessConfigModifiers(open); + } + }); + } + + @NotNull + private CompletableFuture executeProcessConfigModifiers( + @NotNull final ContextOpen context + ) { + return this.pipelines.executeProcessConfigModifiers(context).thenCompose(__ -> + this.executeCreateContainer(context, context.buildFinalConfig()) + ); + } + + @NotNull + private CompletableFuture executeCreateContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config + ) { + return this.pipelines.executeCreateContainer(context, config).thenCompose(container -> + this.executeModifyContainer(context, config, container) + ); + } + + @NotNull + private CompletableFuture executeModifyContainer( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + return this.pipelines.executeModifyContainer(context, config, container).thenCompose(pair -> + this.executeLayoutResolution(context, config, pair.second().container()) + ); + } + + @NotNull + private CompletableFuture executeLayoutResolution( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container + ) { + return this.pipelines.executeLayoutResolution(context, config, container).thenCompose( + pair -> this.executeCreateRender(context, config, container, pair.second()) + ); + } + + @NotNull + private CompletableFuture executeCreateRender( + @NotNull final ContextBase context, + @NotNull final ViewConfig config, + @NotNull final ViewContainer container, + @NotNull final Map layouts + ) { + return this.pipelines.executeCreateRender(context, config, container, layouts).thenCompose( + render -> ((ContextRenderRich) render).simulateFirstRender().thenApply(__ -> render) + ); + } +} diff --git a/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorBukkit.java b/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorBukkit.java new file mode 100644 index 0000000..22d1fc0 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorBukkit.java @@ -0,0 +1,40 @@ +package net.infumia.frame.view.creator; + +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class InventoryCreatorBukkit implements InventoryCreator { + + public static final InventoryCreator INSTANCE = new InventoryCreatorBukkit(); + + private InventoryCreatorBukkit() {} + + @NotNull + @Override + public Inventory create( + @Nullable final InventoryHolder holder, + @NotNull final InventoryType type, + final int size, + @Nullable final Object title + ) { + @NotNull + final String titleAsText; + if (title instanceof String && !((String) title).isEmpty()) { + titleAsText = (String) title; + } else if (title != null) { + throw new IllegalArgumentException( + String.format("Title must be only either String or null '%s'", title) + ); + } else { + titleAsText = type.getDefaultTitle(); + } + + return size == 0 + ? Bukkit.createInventory(holder, type, titleAsText) + : Bukkit.createInventory(holder, size, titleAsText); + } +} diff --git a/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorPaper.java b/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorPaper.java new file mode 100644 index 0000000..760e7af --- /dev/null +++ b/core/src/main/java/net/infumia/frame/view/creator/InventoryCreatorPaper.java @@ -0,0 +1,42 @@ +package net.infumia.frame.view.creator; + +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class InventoryCreatorPaper implements InventoryCreator { + + public static final InventoryCreator INSTANCE = new InventoryCreatorPaper(); + + private InventoryCreatorPaper() {} + + @NotNull + @Override + public Inventory create( + @Nullable final InventoryHolder holder, + @NotNull final InventoryType type, + final int size, + @Nullable final Object title + ) { + if (title instanceof String) { + return InventoryCreatorBukkit.INSTANCE.create(holder, type, size, title); + } + final Component finalTitle; + if (title instanceof Component) { + finalTitle = (Component) title; + } else if (title != null) { + throw new IllegalArgumentException( + String.format("Title must be only either Component or null '%s'", title) + ); + } else { + finalTitle = type.defaultTitle(); + } + return size == 0 + ? Bukkit.createInventory(holder, type, finalTitle) + : Bukkit.createInventory(holder, size, finalTitle); + } +} diff --git a/core/src/main/java/net/infumia/frame/viewer/ContextualViewerImpl.java b/core/src/main/java/net/infumia/frame/viewer/ContextualViewerImpl.java new file mode 100644 index 0000000..5f98abf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/viewer/ContextualViewerImpl.java @@ -0,0 +1,23 @@ +package net.infumia.frame.viewer; + +import net.infumia.frame.context.view.ContextRender; +import org.jetbrains.annotations.NotNull; + +public final class ContextualViewerImpl extends ViewerImpl implements ContextualViewer, Viewer { + + private final ContextRender context; + + public ContextualViewerImpl( + @NotNull final Viewer viewer, + @NotNull final ContextRender context + ) { + super(viewer); + this.context = context; + } + + @NotNull + @Override + public ContextRender context() { + return this.context; + } +} diff --git a/core/src/main/java/net/infumia/frame/viewer/ViewerCreatorImpl.java b/core/src/main/java/net/infumia/frame/viewer/ViewerCreatorImpl.java new file mode 100644 index 0000000..09caef4 --- /dev/null +++ b/core/src/main/java/net/infumia/frame/viewer/ViewerCreatorImpl.java @@ -0,0 +1,21 @@ +package net.infumia.frame.viewer; + +import net.infumia.frame.metadata.MetadataAccessFactory; +import net.infumia.frame.view.View; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public final class ViewerCreatorImpl implements ViewerCreator { + + private final MetadataAccessFactory metadataAccessFactory; + + public ViewerCreatorImpl(@NotNull final MetadataAccessFactory metadataAccessFactory) { + this.metadataAccessFactory = metadataAccessFactory; + } + + @NotNull + @Override + public Viewer create(@NotNull final Player player, @NotNull final View view) { + return new ViewerImpl(player, view, this.metadataAccessFactory.getOrCreate(player)); + } +} diff --git a/core/src/main/java/net/infumia/frame/viewer/ViewerImpl.java b/core/src/main/java/net/infumia/frame/viewer/ViewerImpl.java new file mode 100644 index 0000000..8d34acf --- /dev/null +++ b/core/src/main/java/net/infumia/frame/viewer/ViewerImpl.java @@ -0,0 +1,77 @@ +package net.infumia.frame.viewer; + +import java.util.Objects; +import net.infumia.frame.metadata.MetadataAccess; +import net.infumia.frame.view.View; +import net.infumia.frame.view.ViewContainer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class ViewerImpl implements Viewer { + + private final Player player; + private final View view; + private final MetadataAccess metadata; + + public ViewerImpl( + @NotNull final Player player, + @NotNull final View view, + @NotNull final MetadataAccess metadata + ) { + this.player = player; + this.view = view; + this.metadata = metadata; + } + + public ViewerImpl(@NotNull final Viewer viewer) { + this(viewer.player(), viewer.view(), viewer.metadata()); + } + + @NotNull + @Override + public View view() { + return this.view; + } + + @NotNull + @Override + public Player player() { + return this.player; + } + + @Override + public void close() { + if (this.player.isOnline()) { + this.player.closeInventory(); + } + } + + @Override + public void open(@NotNull final ViewContainer container) { + if (this.player.isOnline()) { + this.player.openInventory(container.inventory()); + } + } + + @NotNull + @Override + public MetadataAccess metadata() { + return this.metadata; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.player); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ViewerImpl)) { + return false; + } + return Objects.equals(this.player, ((Viewer) o).player()); + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 53fc9b7..698413f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,3 +1,9 @@ [libraries] +geantyref = { module = "io.leangen.geantyref:geantyref", version = "1.3.15" } +guice = { module = "com.google.inject:guice", version = "7.0.0" } +adventure-api = { module = "net.kyori:adventure-api", version = "4.17.0" } + +minecraft-one-eight-eight-paper = { module = "com.destroystokyo.paper:paper-api", version = "1.16.5-R0.1-SNAPSHOT" } + nexus-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version = "0.29.0" } spotless-plugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "6.25.0" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 51908f6..01cfba1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,4 +2,4 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" rootProject.name = "frame" -include("common") +include("common", "core")