This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch 3937-grid.api.overhaul in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 012be077069e5bdc37f6feff15c2c5ed9ea1fd13 Author: Andi Huber <[email protected]> AuthorDate: Mon Oct 27 16:06:29 2025 +0100 CAUSEWAY-2297: rename, move and encapsulate --- .../applib/services/grid/GridLoaderService.java | 89 ----- .../applib/services/grid/GridMarshaller.java | 5 +- .../causeway/applib/services/grid/GridService.java | 23 +- .../applib/services/grid/GridSystemService.java | 10 +- core/metamodel/src/main/java/module-info.java | 1 - .../metamodel/CausewayModuleCoreMetamodel.java | 10 +- .../core/metamodel/services/grid/GridCache.java | 190 ++++++++++ .../grid/{bootstrap => }/GridFallbackLayout.xml | 0 .../{bootstrap => }/GridInitializationModel.java | 2 +- .../core/metamodel/services/grid/GridLoader.java | 107 ++++++ .../services/grid/GridLoaderServiceDefault.java | 244 ------------- ...erviceBootstrap.java => GridMarshallerXml.java} | 16 +- .../services/grid/GridServiceDefault.java | 29 +- .../services/grid/GridSystemServiceAbstract.java | 404 --------------------- .../GridSystemServiceBootstrap.java | 360 +++++++++++++++++- .../grid/XsiSchemaLocationProviderForGrid.java | 2 +- .../{bootstrap => }/_UnreferencedSequenceUtil.java | 2 +- .../services/grid/spi/LayoutResource.java | 4 +- .../services/grid/spi/LayoutResourceLoader.java | 14 +- .../grid/spi/LayoutResourceLoaderDefault.java | 7 +- ...meTest.java => GridCache_resourceNameTest.java} | 24 +- .../metamodel/services/grid/GridLoadingTest.java | 27 +- .../mmtestsupport/MetaModelContext_forTesting.java | 22 +- 23 files changed, 736 insertions(+), 856 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java deleted file mode 100644 index bd892fb882e..00000000000 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.causeway.applib.services.grid; - -import java.util.EnumSet; -import java.util.Optional; - -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; - -import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; -import org.apache.causeway.applib.mixins.metamodel.Object_rebuildMetamodel; -import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; - -/** - * Loads the XML layout (grid) for a domain class. - * - * @since 1.x - revised for 2.0 {@index} - */ -public interface GridLoaderService { - - /** - * Whether dynamic reloading of layouts is enabled. - * - * <p> The default implementation enables reloading for prototyping mode, - * disables in production - */ - boolean supportsReloading(); - - /** - * To support metamodel invalidation/rebuilding of spec. - * - * <p>This is called by the {@link Object_rebuildMetamodel} mixin action. - */ - void remove(Class<?> domainClass); - - /** - * Whether any persisted layout metadata (eg a <code>.layout.xml</code> file) exists for this domain class. - * - * <p>If none exists, will return null (and the calling {@link GridService} will use {@link GridSystemService} - * to obtain a default grid for the domain class). - */ - boolean existsFor(Class<?> domainClass, EnumSet<CommonMimeType> supportedFormats); - - /** - * Optionally returns a new instance of a {@link BSGrid}, - * based on whether the underlying resource could be found, loaded and parsed. - * - * <p>The layout alternative will typically be specified through a - * `layout()` method on the domain object, the value of which is used - * for the suffix of the layout file (eg "Customer-layout.archived.xml" - * to use a different layout for customers that have been archived). - * - * @throws UnsupportedOperationException - when format is not supported - */ - Optional<BSGrid> load( - Class<?> domainClass, - @Nullable String layoutIfAny, - @NonNull GridMarshaller marshaller); - - /** - * Optionally returns a new instance of a {@link BSGrid}, - * based on whether the underlying resource could be found, loaded and parsed. - * - * @throws UnsupportedOperationException - when format is not supported - */ - default Optional<BSGrid> load( - final Class<?> domainClass, - final @NonNull GridMarshaller marshaller) { - return load(domainClass, null, marshaller); - } - -} diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java index 1fa32a5234f..2ffb317d705 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java @@ -24,6 +24,7 @@ import org.jspecify.annotations.Nullable; import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; +import org.apache.causeway.applib.services.layout.LayoutService; import org.apache.causeway.applib.services.marshal.Marshaller; import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; import org.apache.causeway.commons.functional.Try; @@ -37,11 +38,11 @@ */ public interface GridMarshaller { - Class<BSGrid> supportedClass(); - /** * Supported format(s) for {@link #unmarshal(Class, String, CommonMimeType)} * and {@link #marshal(BSGrid, CommonMimeType)}. + * + * @apiNote also used by {@link LayoutService} to lookup different formats */ EnumSet<CommonMimeType> supportedFormats(); diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java index 2be3e9d9046..df396a12c1e 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java @@ -29,33 +29,26 @@ /** * Loads the layout (grid) for any domain class. * - * <p> Acts on top of {@link GridLoaderService}, {@link GridMarshaller} and any {@link GridSystemService}(s) registered with Spring. - * @since 1.x {@index} + * <p> Acts on top of {@link GridMarshaller} and any {@link GridSystemService}(s) registered with Spring. + * + * @since 1.x revised for 4.0 {@index} */ public interface GridService { /** * Whether dynamic reloading of layouts is enabled. * - * <p> The default implementation just delegates to the configured - * {@link GridLoaderService}; the default implementation of <i>that</i> - * service enables reloading while prototyping, disables in production. + * <p> The default implementation enables reloading while prototyping, disables in production. */ boolean supportsReloading(); /** * To support metamodel invalidation/rebuilding of spec. - * - * <p>The default implementation just delegates to the configured - * {@link GridLoaderService}. */ void remove(Class<?> domainClass); /** * Whether any persisted layout metadata (eg a <code>.layout.xml</code> file) exists for this domain class. - * - * <p>The default implementation just delegates to the configured - * {@link GridLoaderService}. */ boolean existsFor(Class<?> domainClass); @@ -67,8 +60,6 @@ public interface GridService { * use {@link GridService#defaultGridFor(Class)} to obtain a * default grid if necessary). * - * <p>The default implementation just delegates to the configured - * {@link GridLoaderService}. */ BSGrid load(final Class<?> domainClass); @@ -79,9 +70,7 @@ public interface GridService { * domain object's <code>layout()</code> method, whereby - based on the * state of the domain object - it requests a different layout be used. * - * <p>The default implementation just delegates to the configured - * {@link GridLoaderService}; the default implementation of <i>that</i> - * service uses the layout name to search for a differently + * <p>The default implementation uses the layout name to search for a differently * named layout file, <code>[domainClass].layout.[layout].xml</code>. */ BSGrid load(Class<?> domainClass, String layout); @@ -134,7 +123,7 @@ public interface GridService { // -- LAYOUT EXPORT - GridMarshaller marshaller(); //TODO rename + GridMarshaller marshaller(); default BSGrid toGridForExport( final Class<?> domainClass, diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java index 2a0ec6532b1..bef92c7cc4e 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java @@ -29,18 +29,10 @@ * of the grid) and in providing a default grid if there is no other metadata * available. * - * @since 1.x {@index} + * @since 1.x revised for 4.0 {@index} */ public interface GridSystemService { - /** - * The concrete subclass of {@link BSGrid} supported by this implementation. - * - * <p>There can be multiple implementations of this service, this indicates - * the base class used by the implementation. - */ - Class<BSGrid> gridImplementation(); - /** * A default grid, used when no grid layout can be found for the domain * class. diff --git a/core/metamodel/src/main/java/module-info.java b/core/metamodel/src/main/java/module-info.java index bce3eed5164..49ed9a4f34a 100644 --- a/core/metamodel/src/main/java/module-info.java +++ b/core/metamodel/src/main/java/module-info.java @@ -97,7 +97,6 @@ exports org.apache.causeway.core.metamodel.services.devutils; exports org.apache.causeway.core.metamodel.services.events; exports org.apache.causeway.core.metamodel.services.exceprecog; - exports org.apache.causeway.core.metamodel.services.grid.bootstrap; exports org.apache.causeway.core.metamodel.services.grid; exports org.apache.causeway.core.metamodel.services.idstringifier; exports org.apache.causeway.core.metamodel.services.ixn; diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java index eff931b9e05..8231c1b4ebb 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java @@ -48,11 +48,9 @@ import org.apache.causeway.core.metamodel.services.columnorder.ColumnOrderTxtFileServiceDefault; import org.apache.causeway.core.metamodel.services.events.MetamodelEventService; import org.apache.causeway.core.metamodel.services.exceprecog.ExceptionRecognizerForRecoverableException; -import org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault; +import org.apache.causeway.core.metamodel.services.grid.GridMarshallerXml; import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault; -import org.apache.causeway.core.metamodel.services.grid.XsiSchemaLocationProviderForGrid; -import org.apache.causeway.core.metamodel.services.grid.bootstrap.GridMarshallerServiceBootstrap; -import org.apache.causeway.core.metamodel.services.grid.bootstrap.GridSystemServiceBootstrap; +import org.apache.causeway.core.metamodel.services.grid.GridSystemServiceBootstrap; import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault; import org.apache.causeway.core.metamodel.services.idstringifier.IdStringifierLookupService; import org.apache.causeway.core.metamodel.services.inject.ServiceInjectorDefault; @@ -173,9 +171,7 @@ // @Service's ColumnOrderTxtFileServiceDefault.class, ExceptionRecognizerForRecoverableException.class, - XsiSchemaLocationProviderForGrid.class, - GridLoaderServiceDefault.class, - GridMarshallerServiceBootstrap.class, + GridMarshallerXml.class, GridServiceDefault.class, GridSystemServiceBootstrap.class, IdStringifierLookupService.class, diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java new file mode 100644 index 00000000000..e75c2a80565 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.causeway.core.metamodel.services.grid; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.jspecify.annotations.NonNull; + +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; +import org.apache.causeway.applib.mixins.metamodel.Object_rebuildMetamodel; +import org.apache.causeway.applib.services.grid.GridMarshaller; +import org.apache.causeway.applib.services.grid.GridService; +import org.apache.causeway.applib.services.grid.GridSystemService; +import org.apache.causeway.applib.services.message.MessageService; +import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; +import org.apache.causeway.commons.collections.Can; +import org.apache.causeway.core.metamodel.services.grid.GridLoader.LayoutKey; +import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader; + +import lombok.extern.slf4j.Slf4j; + +/** + * Cache for {@link BSGrid} instances, + * delegating grid loading to the {@link GridLoader}. + * + * @since 4.0 + */ +@Slf4j +record GridCache( + GridLoader gridLoader, + MessageService messageService, + /** + * Whether dynamic reloading of layouts is enabled. + * + * <p> The default implementation enables reloading for prototyping mode, + * disables in production + */ + boolean supportsReloading, + Map<LayoutKey, BSGrid> gridCache, + // for better logging messages (used only in prototyping mode) + Map<LayoutKey, String> badContentByKey) { + + public GridCache( + final MessageService messageService, + final boolean supportsReloading, + final List<LayoutResourceLoader> layoutResourceLoaders) { + this(new GridLoader(Can.ofCollection(layoutResourceLoaders)), + messageService, + supportsReloading, + new HashMap<>(), new HashMap<>()); + } + + /** + * To support metamodel invalidation/rebuilding of spec. + * Acts as a no-op if reloading is not supported. + * + * <p>This is called by the {@link Object_rebuildMetamodel} mixin action. + */ + public void remove(final Class<?> domainClass) { + if(!supportsReloading()) return; + + final String layoutIfAny = null; + var layoutKey = new LayoutKey(domainClass, layoutIfAny); + badContentByKey.remove(layoutKey); + gridCache.remove(layoutKey); + } + + /** + * Whether any persisted layout metadata (eg a <code>.layout.xml</code> file) exists for this domain class. + * + * <p>If none exists, will return null (and the calling {@link GridService} will use {@link GridSystemService} + * to obtain a default grid for the domain class). + */ + public boolean existsFor(final Class<?> domainClass, final EnumSet<CommonMimeType> supportedFormats) { + return gridLoader.loadLayoutResource(new LayoutKey(domainClass, null), supportedFormats).isPresent(); + } + + /** + * Optionally returns a new instance of a {@link BSGrid}, + * based on whether the underlying resource could be found, loaded and parsed. + * + * <p>The layout alternative will typically be specified through a + * `layout()` method on the domain object, the value of which is used + * for the suffix of the layout file (eg "Customer-layout.archived.xml" + * to use a different layout for customers that have been archived). + * + * @throws UnsupportedOperationException - when format is not supported + */ + public Optional<BSGrid> load( + final Class<?> domainClass, + final String layoutIfAny, + final @NonNull GridMarshaller marshaller) { + + var supportedFormats = marshaller.supportedFormats(); + + var layoutKey = new LayoutKey(domainClass, layoutIfAny); + var layoutResource = gridLoader.loadLayoutResource(layoutKey, supportedFormats).orElse(null); + if(layoutResource == null) { + log.debug( + "Failed to locate or load layout resource for class {}, " + + "with layout-suffix (if any) {}, " + + "using layout-resource-loaders {}.", + domainClass.getName(), layoutIfAny, + gridLoader().layoutResourceLoaders().stream() + .map(Object::getClass) + .map(Class::getName) + .collect(Collectors.joining(", "))); + return Optional.empty(); + } + + if(supportsReloading()) { + final String badContent = badContentByKey.get(layoutKey); + if(badContent != null) { + if(Objects.equals(layoutResource.content(), badContent)) { + // seen this before and already logged; just quit + return Optional.empty(); + } else { + // this different content might be good + badContentByKey.remove(layoutKey); + } + } + } else { + // if cached, serve from cache - otherwise fall through + final BSGrid grid = gridCache.get(layoutKey); + if(grid != null) return Optional.of(grid); + } + + try { + final BSGrid grid = marshaller + .unmarshal(domainClass, layoutResource.content(), layoutResource.format()) + .getValue().orElseThrow(); + if(supportsReloading()) { + gridCache.put(layoutKey, grid); + } + return Optional.of(grid); + } catch(Exception ex) { + + if(supportsReloading()) { + // save fact that this was bad content, so that we don't log again if called next time + badContentByKey.put(layoutKey, layoutResource.content()); + } + + // note that we don't blacklist if the file exists but couldn't be parsed; + // the developer might fix so we will want to retry. + final String resourceName = layoutResource.resourceName(); + final String message = "Failed to parse " + resourceName + " file (" + ex.getMessage() + ")"; + if(supportsReloading()) { + messageService.warnUser(message); + } + log.warn(message); + + return Optional.empty(); + } + } + + /** + * Optionally returns a new instance of a {@link BSGrid}, + * based on whether the underlying resource could be found, loaded and parsed. + * + * @throws UnsupportedOperationException - when format is not supported + */ + public Optional<BSGrid> load( + final Class<?> domainClass, + final @NonNull GridMarshaller marshaller) { + return load(domainClass, null, marshaller); + } + +} diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridFallbackLayout.xml b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridFallbackLayout.xml similarity index 100% rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridFallbackLayout.xml rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridFallbackLayout.xml diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridInitializationModel.java similarity index 99% rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridInitializationModel.java index 7f88800c6c3..92bed33d96a 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridInitializationModel.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.causeway.core.metamodel.services.grid.bootstrap; +package org.apache.causeway.core.metamodel.services.grid; import java.util.Collection; import java.util.LinkedHashMap; diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoader.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoader.java new file mode 100644 index 00000000000..094fe1122ad --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoader.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.causeway.core.metamodel.services.grid; + +import java.util.EnumSet; +import java.util.Optional; +import java.util.stream.Stream; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; +import org.apache.causeway.commons.collections.Can; +import org.apache.causeway.commons.internal.base._Strings; +import org.apache.causeway.commons.internal.reflection._Reflect; +import org.apache.causeway.commons.internal.reflection._Reflect.InterfacePolicy; +import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResource; +import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader; + +record GridLoader( + Can<LayoutResourceLoader> layoutResourceLoaders) { + + public record LayoutKey( + @NonNull Class<?> domainClass, + /** layout suffix */ + @Nullable String layoutIfAny) { + } + + // -- HELPER + + Optional<LayoutResource> loadLayoutResource( + final LayoutKey layoutKey, + final EnumSet<CommonMimeType> supportedFormats) { + return _Reflect.streamTypeHierarchy(layoutKey.domainClass(), InterfacePolicy.EXCLUDE) + .flatMap(type->loadContent(type, layoutKey.layoutIfAny(), supportedFormats).stream()) + .findFirst(); + } + + private Optional<LayoutResource> loadContent( + final @NonNull Class<?> domainClass, + final @Nullable String layoutIfAny, + final EnumSet<CommonMimeType> supportedFormats) { + return streamResourceNameCandidatesFor(domainClass, layoutIfAny, supportedFormats) + .flatMap(candidateResourceName->lookupLayoutResourceUsingLoaders(domainClass, candidateResourceName).stream()) + .findFirst(); + } + + private Stream<String> streamResourceNameCandidatesFor( + final @NonNull Class<?> domainClass, + final @Nullable String layoutIfAny, + final @NonNull EnumSet<CommonMimeType> supportedFormats) { + return supportedFormats.stream() + .flatMap(format->streamResourceNameCandidatesFor(domainClass, layoutIfAny, format)); + } + + private Stream<String> streamResourceNameCandidatesFor( + final @NonNull Class<?> domainClass, + final @Nullable String layoutIfAny, + final @NonNull CommonMimeType format) { + return format.proposedFileExtensions().stream() + .flatMap(fileExtension->streamResourceNameCandidatesFor(domainClass, layoutIfAny, fileExtension)); + } + + private Stream<String> streamResourceNameCandidatesFor( + final @NonNull Class<?> domainClass, + final @Nullable String layoutIfAny, + final @NonNull String fileExtension) { + + var typeSimpleName = domainClass.getSimpleName(); + + return _Strings.isNotEmpty(layoutIfAny) + ? Stream.of( + String.format("%s-%s.layout.%s", typeSimpleName, layoutIfAny, fileExtension), + String.format("%s.layout.%s", typeSimpleName, fileExtension), + String.format("%s.layout.fallback.%s", typeSimpleName, fileExtension)) + : Stream.of( + String.format("%s.layout.%s", typeSimpleName, fileExtension), + String.format("%s.layout.fallback.%s", typeSimpleName,fileExtension)); + } + + private Optional<LayoutResource> lookupLayoutResourceUsingLoaders( + final @NonNull Class<?> type, + final @NonNull String candidateResourceName) { + + return layoutResourceLoaders.stream() + .flatMap(loader->loader.lookupLayoutResource(type, candidateResourceName).stream()) + .findFirst(); + } + + +} diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java deleted file mode 100644 index 599495ade14..00000000000 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.causeway.core.metamodel.services.grid; - -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import jakarta.annotation.Priority; -import jakarta.inject.Inject; -import jakarta.inject.Named; - -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -import org.apache.causeway.applib.annotation.PriorityPrecedence; -import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; -import org.apache.causeway.applib.services.grid.GridLoaderService; -import org.apache.causeway.applib.services.grid.GridMarshaller; -import org.apache.causeway.applib.services.message.MessageService; -import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; -import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.commons.internal.base._Strings; -import org.apache.causeway.commons.internal.collections._Maps; -import org.apache.causeway.commons.internal.reflection._Reflect; -import org.apache.causeway.commons.internal.reflection._Reflect.InterfacePolicy; -import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; -import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; -import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResource; -import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import lombok.extern.slf4j.Slf4j; - -/** - * Default implementation of {@link GridLoaderService}. - * - * @since 1.x revised for 2.0 {@index} - */ -@Service -@Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridLoaderServiceDefault") -@Priority(PriorityPrecedence.MIDPOINT) -@Qualifier("Default") -@RequiredArgsConstructor //JUnit Support -@Slf4j -public class GridLoaderServiceDefault implements GridLoaderService { - - private final MessageService messageService; - final Can<LayoutResourceLoader> layoutResourceLoaders; - - @Getter(onMethod_={@Override}) @Accessors(fluent = true) - private final boolean supportsReloading; - - @Inject - public GridLoaderServiceDefault( - final MessageService messageService, - final CausewaySystemEnvironment causewaySystemEnvironment, - final List<LayoutResourceLoader> layoutResourceLoaders) { - this.messageService = messageService; - this.supportsReloading = causewaySystemEnvironment.isPrototyping(); - this.layoutResourceLoaders = Can.ofCollection(layoutResourceLoaders); - } - - record LayoutKey( - @NonNull Class<?> domainClass, - /** layout suffix */ - @Nullable String layoutIfAny) { - } - - // for better logging messages (used only in prototyping mode) - private final Map<LayoutKey, String> badContentByKey = _Maps.newHashMap(); - // cache (used only in prototyping mode) - private final Map<LayoutKey, BSGrid> gridCache = _Maps.newHashMap(); - - @Override - public void remove(final Class<?> domainClass) { - if(!supportsReloading()) { - return; - } - final String layoutIfAny = null; - var layoutKey = new LayoutKey(domainClass, layoutIfAny); - badContentByKey.remove(layoutKey); - gridCache.remove(layoutKey); - } - - @Override - public boolean existsFor(final Class<?> domainClass, final EnumSet<CommonMimeType> supportedFormats) { - return loadLayoutResource(new LayoutKey(domainClass, null), supportedFormats).isPresent(); - } - - @Override - public Optional<BSGrid> load( - final Class<?> domainClass, - final String layoutIfAny, - final @NonNull GridMarshaller marshaller) { - - var supportedFormats = marshaller.supportedFormats(); - - var layoutKey = new LayoutKey(domainClass, layoutIfAny); - var layoutResource = loadLayoutResource(layoutKey, supportedFormats).orElse(null); - if(layoutResource == null) { - log.debug( - "Failed to locate or load layout resource for class {}, " - + "with layout-suffix (if any) {}, " - + "using layout-resource-loaders {}.", - domainClass.getName(), layoutIfAny, - layoutResourceLoaders.stream().map(Object::getClass).map(Class::getName).collect(Collectors.joining(", "))); - return Optional.empty(); - } - - if(supportsReloading()) { - final String badContent = badContentByKey.get(layoutKey); - if(badContent != null) { - if(Objects.equals(layoutResource.content(), badContent)) { - // seen this before and already logged; just quit - return Optional.empty(); - } else { - // this different content might be good - badContentByKey.remove(layoutKey); - } - } - } else { - // if cached, serve from cache - otherwise fall through - final BSGrid grid = gridCache.get(layoutKey); - if(grid != null) { - return Optional.of(grid); - } - } - - try { - final BSGrid grid = marshaller - .unmarshal(domainClass, layoutResource.content(), layoutResource.format()) - .getValue().orElseThrow(); - if(supportsReloading()) { - gridCache.put(layoutKey, grid); - } - return Optional.of(grid); - } catch(Exception ex) { - - if(supportsReloading()) { - // save fact that this was bad content, so that we don't log again if called next time - badContentByKey.put(layoutKey, layoutResource.content()); - } - - // note that we don't blacklist if the file exists but couldn't be parsed; - // the developer might fix so we will want to retry. - final String resourceName = layoutResource.resourceName(); - final String message = "Failed to parse " + resourceName + " file (" + ex.getMessage() + ")"; - if(supportsReloading()) { - messageService.warnUser(message); - } - log.warn(message); - - return Optional.empty(); - } - } - - // -- HELPER - - Optional<LayoutResource> loadLayoutResource( - final LayoutKey layoutKey, - final EnumSet<CommonMimeType> supportedFormats) { - return _Reflect.streamTypeHierarchy(layoutKey.domainClass(), InterfacePolicy.EXCLUDE) - .flatMap(type->loadContent(type, layoutKey.layoutIfAny(), supportedFormats).stream()) - .findFirst(); - } - - private Optional<LayoutResource> loadContent( - final @NonNull Class<?> domainClass, - final @Nullable String layoutIfAny, - final EnumSet<CommonMimeType> supportedFormats) { - return streamResourceNameCandidatesFor(domainClass, layoutIfAny, supportedFormats) - .flatMap(candidateResourceName->lookupLayoutResourceUsingLoaders(domainClass, candidateResourceName).stream()) - .findFirst(); - } - - private Stream<String> streamResourceNameCandidatesFor( - final @NonNull Class<?> domainClass, - final @Nullable String layoutIfAny, - final @NonNull EnumSet<CommonMimeType> supportedFormats) { - return supportedFormats.stream() - .flatMap(format->streamResourceNameCandidatesFor(domainClass, layoutIfAny, format)); - } - - private Stream<String> streamResourceNameCandidatesFor( - final @NonNull Class<?> domainClass, - final @Nullable String layoutIfAny, - final @NonNull CommonMimeType format) { - return format.proposedFileExtensions().stream() - .flatMap(fileExtension->streamResourceNameCandidatesFor(domainClass, layoutIfAny, fileExtension)); - } - - private Stream<String> streamResourceNameCandidatesFor( - final @NonNull Class<?> domainClass, - final @Nullable String layoutIfAny, - final @NonNull String fileExtension) { - - var typeSimpleName = domainClass.getSimpleName(); - - return _Strings.isNotEmpty(layoutIfAny) - ? Stream.of( - String.format("%s-%s.layout.%s", typeSimpleName, layoutIfAny, fileExtension), - String.format("%s.layout.%s", typeSimpleName, fileExtension), - String.format("%s.layout.fallback.%s", typeSimpleName, fileExtension)) - : Stream.of( - String.format("%s.layout.%s", typeSimpleName, fileExtension), - String.format("%s.layout.fallback.%s", typeSimpleName,fileExtension)); - } - - private Optional<LayoutResource> lookupLayoutResourceUsingLoaders( - final @NonNull Class<?> type, - final @NonNull String candidateResourceName) { - - return layoutResourceLoaders.stream() - .flatMap(loader->loader.lookupLayoutResource(type, candidateResourceName).stream()) - .findFirst(); - } - -} diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridMarshallerXml.java similarity index 86% rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridMarshallerXml.java index 36fd1f48a59..d2ef96040e3 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridMarshallerXml.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.causeway.core.metamodel.services.grid.bootstrap; +package org.apache.causeway.core.metamodel.services.grid; import java.util.EnumSet; import java.util.Map; @@ -42,7 +42,6 @@ import org.apache.causeway.commons.internal.exceptions._Exceptions; import org.apache.causeway.commons.io.JaxbUtils; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; -import org.apache.causeway.core.metamodel.services.grid.XsiSchemaLocationProviderForGrid; /** * Default implementation of {@link GridMarshaller} using DTOs based on @@ -51,18 +50,18 @@ * @since 2.0 {@index} */ @Service -@Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridMarshallerServiceBootstrap") +@Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridMarshallerXml") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -public record GridMarshallerServiceBootstrap( +public record GridMarshallerXml( JaxbService jaxbService, XsiSchemaLocationProviderForGrid schemaLocationProvider, EnumSet<CommonMimeType> supportedFormats ) implements GridMarshaller { @Inject - public GridMarshallerServiceBootstrap(final JaxbService jaxbService, final XsiSchemaLocationProviderForGrid schemaLocationProvider) { - this(jaxbService, schemaLocationProvider, EnumSet.of(CommonMimeType.XML)); + public GridMarshallerXml(final JaxbService jaxbService) { + this(jaxbService, new XsiSchemaLocationProviderForGrid(), EnumSet.of(CommonMimeType.XML)); // eagerly create a JAXBContext for this grid type (and cache it) JaxbUtils.jaxbContextFor(BSGrid.class, true); } @@ -72,11 +71,6 @@ public EnumSet<CommonMimeType> supportedFormats() { return supportedFormats; } - @Override - public Class<BSGrid> supportedClass() { - return BSGrid.class; - } - @Override public String marshal(final @NonNull BSGrid bsGrid, final @NonNull CommonMimeType format) { throwIfFormatNotSupported(format); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java index 00a234e5eae..55e64297794 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java @@ -21,6 +21,7 @@ import java.util.List; import jakarta.annotation.Priority; +import jakarta.inject.Inject; import jakarta.inject.Named; import org.springframework.beans.factory.annotation.Qualifier; @@ -28,12 +29,14 @@ import org.apache.causeway.applib.annotation.PriorityPrecedence; import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; -import org.apache.causeway.applib.services.grid.GridLoaderService; import org.apache.causeway.applib.services.grid.GridMarshaller; import org.apache.causeway.applib.services.grid.GridService; import org.apache.causeway.applib.services.grid.GridSystemService; +import org.apache.causeway.applib.services.message.MessageService; import org.apache.causeway.commons.internal.base._Casts; +import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; +import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader; /** * Default implementation of {@link GridService}. @@ -45,33 +48,43 @@ @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") public record GridServiceDefault( - GridLoaderService gridLoaderService, GridMarshaller marshaller, - List<GridSystemService> gridSystemServices) implements GridService { + List<GridSystemService> gridSystemServices, + GridCache gridCache) implements GridService { + + @Inject + public GridServiceDefault( + final CausewaySystemEnvironment causewaySystemEnvironment, + final GridMarshaller marshaller, + final MessageService messageService, + final List<GridSystemService> gridSystemServices, + final List<LayoutResourceLoader> layoutResourceLoaders) { + this(marshaller, gridSystemServices, new GridCache(messageService, causewaySystemEnvironment.isPrototyping(), layoutResourceLoaders)); + } @Override public boolean supportsReloading() { - return gridLoaderService.supportsReloading(); + return gridCache.supportsReloading(); } @Override public void remove(final Class<?> domainClass) { - gridLoaderService.remove(domainClass); + gridCache.remove(domainClass); } @Override public boolean existsFor(final Class<?> domainClass) { - return gridLoaderService.existsFor(domainClass, marshaller.supportedFormats()); + return gridCache.existsFor(domainClass, marshaller.supportedFormats()); } @Override public BSGrid load(final Class<?> domainClass) { - return gridLoaderService.load(domainClass, marshaller).orElse(null); + return gridCache.load(domainClass, marshaller).orElse(null); } @Override public BSGrid load(final Class<?> domainClass, final String layout) { - return gridLoaderService.load(domainClass, layout, marshaller).orElse(null); + return gridCache.load(domainClass, layout, marshaller).orElse(null); } // -- diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java deleted file mode 100644 index 82f89a3c5d7..00000000000 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.causeway.core.metamodel.services.grid; - -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; - -import jakarta.inject.Inject; -import jakarta.inject.Provider; - -import org.apache.causeway.applib.annotation.Programmatic; -import org.apache.causeway.applib.layout.component.ActionLayoutData; -import org.apache.causeway.applib.layout.component.CollectionLayoutData; -import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; -import org.apache.causeway.applib.layout.component.DomainObjectLayoutDataOwner; -import org.apache.causeway.applib.layout.component.FieldSet; -import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; -import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; -import org.apache.causeway.applib.services.grid.GridSystemService; -import org.apache.causeway.applib.services.i18n.TranslationService; -import org.apache.causeway.applib.services.message.MessageService; -import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; -import org.apache.causeway.core.metamodel.facetapi.Facet; -import org.apache.causeway.core.metamodel.facetapi.FacetUtil; -import org.apache.causeway.core.metamodel.facets.actions.layout.ActionPositionFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.actions.layout.CssClassFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.actions.layout.FaFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.actions.layout.HiddenFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.actions.layout.MemberDescribedFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.actions.layout.MemberNamedFacetForActionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.CssClassFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.DefaultViewFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.HiddenFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.MemberDescribedFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.MemberNamedFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.PagedFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.SortedByFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.collections.layout.tabledec.TableDecoratorFacetForCollectionLayoutXml; -import org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName; -import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacetForLayoutXml; -import org.apache.causeway.core.metamodel.facets.members.layout.order.LayoutOrderFacetForLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.BookmarkPolicyFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.CssClassFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.FaFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.ObjectDescribedFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.ObjectNamedFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.tabledec.TableDecoratorFacetForDomainObjectLayoutXml; -import org.apache.causeway.core.metamodel.facets.object.promptStyle.PromptStyleFacet; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.CssClassFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.HiddenFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.LabelAtFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MemberDescribedFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MemberNamedFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MultiLineFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.RenderedAdjustedFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.TypicalLengthFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.facets.properties.propertylayout.UnchangingFacetForPropertyLayoutXml; -import org.apache.causeway.core.metamodel.layout.LayoutFacetUtil.MetamodelToGridOverridingVisitor; -import org.apache.causeway.core.metamodel.spec.feature.MixedIn; -import org.apache.causeway.core.metamodel.spec.feature.ObjectMember; -import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; - -import static org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacet; -import static org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacetIfPresent; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@RequiredArgsConstructor(onConstructor_ = {@Inject}, access = AccessLevel.PROTECTED) -@Slf4j -public abstract class GridSystemServiceAbstract -implements GridSystemService { - - protected final Provider<SpecificationLoader> specLoaderProvider; - protected final TranslationService translationService; - protected final MessageService messageService; - protected final CausewaySystemEnvironment causewaySystemEnvironment; - - @Override - public void normalize(final BSGrid grid, final Class<?> domainClass) { - - // ignore any other grid implementations - if(!gridImplementation().isAssignableFrom(grid.getClass())) return; - - final boolean valid = validateAndNormalize(grid, domainClass); - if (valid) { - overwriteFacets(grid, domainClass); - if(log.isDebugEnabled()) { - log.debug("Grid:\n\n{}\n\n", toXml(grid)); - } - } else { - if(causewaySystemEnvironment.isPrototyping()) { - messageService.warnUser("Grid metadata errors for " + grid.domainClass().getName() + "; check the error log"); - } - log.error("Grid metadata errors in {}:\n\n{}\n\n", grid.domainClass().getName(), toXml(grid)); - } - } - - protected abstract String toXml(BSGrid grid); - - /** - * Mandatory hook method for subclasses, where they must ensure that all object members (properties, collections - * and actions) are in the grid metadata, typically by deriving this information from other existing metadata - * (eg facets from annotations) or just by applying default rules. - */ - protected abstract boolean validateAndNormalize( - final BSGrid grid, - final Class<?> domainClass); - - /** - * Overwrites (replaces) any existing facets in the metamodel with info taken from the grid. - * - * @implNote This code uses {@link FacetUtil#updateFacet(Facet)} - * because the layout might be reloaded from XML if reloading is supported. - */ - private void overwriteFacets( - final BSGrid fcGrid, - final Class<?> domainClass) { - - var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); - - var oneToOneAssociationById = ObjectMember.mapById(objectSpec.streamProperties(MixedIn.INCLUDED)); - var oneToManyAssociationById = ObjectMember.mapById(objectSpec.streamCollections(MixedIn.INCLUDED)); - var objectActionById = ObjectMember.mapById(objectSpec.streamRuntimeActions(MixedIn.INCLUDED)); - - // governs, whether annotations win over XML grid, based on whether XML grid is fallback or 'explicit' - var precedence = fcGrid.isFallback() - ? Facet.Precedence.LOW // fallback case: XML layout is overruled by layout from annotations - : Facet.Precedence.HIGH; // non-fallback case: XML layout overrules layout from annotations - - final AtomicInteger propertySequence = new AtomicInteger(0); - fcGrid.visit(new BSElementVisitor() { - private int collectionSequence = 1; - - private int actionDomainObjectSequence = 1; - private int actionPropertyGroupSequence = 1; - private int actionPropertySequence = 1; - private int actionCollectionSequence = 1; - - @Override - public void visit(final DomainObjectLayoutData domainObjectLayoutData) { - - updateFacetIfPresent( - BookmarkPolicyFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - updateFacetIfPresent( - CssClassFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - updateFacetIfPresent( - FaFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - updateFacetIfPresent( - ObjectDescribedFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - updateFacetIfPresent( - ObjectNamedFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - updateFacetIfPresent( - TableDecoratorFacetForDomainObjectLayoutXml - .create(domainObjectLayoutData, objectSpec, precedence)); - } - - @Override - public void visit(final ActionLayoutData actionLayoutData) { - - var actionLayoutDataOwner = actionLayoutData.owner(); - var objectAction = objectActionById.get(actionLayoutData.getId()); - if(objectAction == null) return; - - { - GroupIdAndName groupIdAndName = null; - int memberOrderSequence; - if(actionLayoutDataOwner instanceof FieldSet) { - var fieldSet = (FieldSet) actionLayoutDataOwner; - for (var propertyLayoutData : fieldSet.getProperties()) { - // any will do; choose the first one that we know is valid - if(oneToOneAssociationById.containsKey(propertyLayoutData.getId())) { - groupIdAndName = GroupIdAndName - .forPropertyLayoutData(propertyLayoutData) - .orElse(null); - break; - } - } - memberOrderSequence = actionPropertyGroupSequence++; - } else if(actionLayoutDataOwner instanceof PropertyLayoutData) { - groupIdAndName = GroupIdAndName - .forPropertyLayoutData((PropertyLayoutData) actionLayoutDataOwner) - .orElse(null); - memberOrderSequence = actionPropertySequence++; - } else if(actionLayoutDataOwner instanceof CollectionLayoutData) { - groupIdAndName = GroupIdAndName - .forCollectionLayoutData((CollectionLayoutData) actionLayoutDataOwner) - .orElse(null); - memberOrderSequence = actionCollectionSequence++; - } else { - // don't add: any existing metadata should be preserved - groupIdAndName = null; - memberOrderSequence = actionDomainObjectSequence++; - } - updateFacet( - LayoutOrderFacetForLayoutXml.create(memberOrderSequence, objectAction, precedence)); - - //XXX hotfix: always override LayoutGroupFacetFromActionLayoutAnnotation, otherwise actions are not shown - don't know why - var precedenceHotfix = fcGrid.isFallback() - ? Facet.Precedence.DEFAULT - : Facet.Precedence.HIGH; - - updateFacetIfPresent( - LayoutGroupFacetForLayoutXml.create(groupIdAndName, objectAction, precedenceHotfix)); - } - - // fix up the action position if required - if(actionLayoutDataOwner instanceof FieldSet) { - if(actionLayoutData.getPosition() == null || - actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.BELOW || - actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.RIGHT) { - actionLayoutData.setPosition(org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL); - } - } else if(actionLayoutDataOwner instanceof PropertyLayoutData) { - if(actionLayoutData.getPosition() == null || - actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL_DROPDOWN || - actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL) { - actionLayoutData.setPosition(org.apache.causeway.applib.annotation.ActionLayout.Position.BELOW); - } - } else { - // doesn't do anything for DomainObject or Collection - actionLayoutData.setPosition(null); - } - - updateFacetIfPresent( - ActionPositionFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - CssClassFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - FaFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - MemberDescribedFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - HiddenFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - MemberNamedFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); - - updateFacetIfPresent( - Optional.ofNullable(actionLayoutData) - .map(ActionLayoutData::getPromptStyle) - .map(promptStyle->new PromptStyleFacet("ActionLayoutXml", promptStyle, objectAction, precedence, true))); - - } - - @Override - public void visit(final PropertyLayoutData propertyLayoutData) { - var oneToOneAssociation = oneToOneAssociationById.get(propertyLayoutData.getId()); - if(oneToOneAssociation == null) return; - - updateFacetIfPresent( - CssClassFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - MemberDescribedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - HiddenFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - LabelAtFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - MultiLineFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - MemberNamedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - Optional.ofNullable(propertyLayoutData) - .map(PropertyLayoutData::getPromptStyle) - .map(promptStyle->new PromptStyleFacet("PropertyLayoutXml", promptStyle, oneToOneAssociation, precedence, true))); - - updateFacetIfPresent( - RenderedAdjustedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - UnchangingFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - updateFacetIfPresent( - TypicalLengthFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); - - // Layout group-name based on owning property group, Layout sequence monotonically increasing - // nb for any given field set the sequence won't reset to zero; however this is what we want so that - // table columns are shown correctly (by fieldset, then property order within that fieldset). - final FieldSet fieldSet = propertyLayoutData.owner(); - - updateFacet(LayoutOrderFacetForLayoutXml.create(propertySequence.incrementAndGet(), oneToOneAssociation, precedence)); - - updateFacetIfPresent( - LayoutGroupFacetForLayoutXml.create(fieldSet, oneToOneAssociation, precedence)); - } - - @Override - public void visit(final CollectionLayoutData collectionLayoutData) { - var oneToManyAssociation = oneToManyAssociationById.get(collectionLayoutData.getId()); - if(oneToManyAssociation == null) { - return; - } - - updateFacetIfPresent( - CssClassFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - DefaultViewFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - TableDecoratorFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - MemberDescribedFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - HiddenFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - MemberNamedFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - PagedFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacetIfPresent( - SortedByFacetForCollectionLayoutXml - .create(collectionLayoutData, oneToManyAssociation, precedence)); - - updateFacet(LayoutOrderFacetForLayoutXml - .create(collectionSequence++, oneToManyAssociation, precedence)); - } - }); - } - - // -- - - @Programmatic - @Override - public void complete(final BSGrid grid, final Class<?> domainClass) { - normalize(grid, domainClass); - var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); - grid.visit(new MetamodelToGridOverridingVisitor(objectSpec)); - } - - @Programmatic - @Override - public void minimal(final BSGrid grid, final Class<?> domainClass) { - normalize(grid, domainClass); - grid.visit(new BSElementVisitor() { - @Override - public void visit(final ActionLayoutData actionLayoutData) { - actionLayoutData.owner().getActions().remove(actionLayoutData); - } - - @Override - public void visit(final CollectionLayoutData collectionLayoutData) { - collectionLayoutData.owner().getCollections().remove(collectionLayoutData); - } - - @Override - public void visit(final PropertyLayoutData propertyLayoutData) { - propertyLayoutData.owner().getProperties().remove(propertyLayoutData); - } - - @Override - public void visit(final DomainObjectLayoutData domainObjectLayoutData) { - final DomainObjectLayoutDataOwner owner = domainObjectLayoutData.owner(); - owner.setDomainObject(new DomainObjectLayoutData()); - } - }); - } - -} diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java similarity index 58% rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java index a63e106344b..c675b83a9c3 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java @@ -16,14 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.causeway.core.metamodel.services.grid.bootstrap; +package org.apache.causeway.core.metamodel.services.grid; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -44,6 +46,7 @@ import org.apache.causeway.applib.layout.component.ActionLayoutDataOwner; import org.apache.causeway.applib.layout.component.CollectionLayoutData; import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; +import org.apache.causeway.applib.layout.component.DomainObjectLayoutDataOwner; import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; import org.apache.causeway.applib.layout.grid.bootstrap.BSCol; @@ -52,7 +55,9 @@ import org.apache.causeway.applib.layout.grid.bootstrap.BSTab; import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup; import org.apache.causeway.applib.layout.grid.bootstrap.Size; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.applib.services.grid.GridMarshaller; +import org.apache.causeway.applib.services.grid.GridSystemService; import org.apache.causeway.applib.services.i18n.TranslationService; import org.apache.causeway.applib.services.jaxb.JaxbService; import org.apache.causeway.applib.services.message.MessageService; @@ -68,11 +73,45 @@ import org.apache.causeway.core.config.CausewayConfiguration; import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; +import org.apache.causeway.core.metamodel.facetapi.Facet; +import org.apache.causeway.core.metamodel.facetapi.FacetUtil; +import org.apache.causeway.core.metamodel.facets.actions.layout.ActionPositionFacetForActionLayoutXml; +import org.apache.causeway.core.metamodel.facets.actions.layout.CssClassFacetForActionLayoutXml; +import org.apache.causeway.core.metamodel.facets.actions.layout.FaFacetForActionLayoutXml; +import org.apache.causeway.core.metamodel.facets.actions.layout.HiddenFacetForActionLayoutXml; +import org.apache.causeway.core.metamodel.facets.actions.layout.MemberDescribedFacetForActionLayoutXml; +import org.apache.causeway.core.metamodel.facets.actions.layout.MemberNamedFacetForActionLayoutXml; import org.apache.causeway.core.metamodel.facets.actions.position.ActionPositionFacet; +import org.apache.causeway.core.metamodel.facets.collections.layout.CssClassFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.DefaultViewFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.HiddenFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.MemberDescribedFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.MemberNamedFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.PagedFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.SortedByFacetForCollectionLayoutXml; +import org.apache.causeway.core.metamodel.facets.collections.layout.tabledec.TableDecoratorFacetForCollectionLayoutXml; import org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName; import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacet; +import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacetForLayoutXml; +import org.apache.causeway.core.metamodel.facets.members.layout.order.LayoutOrderFacetForLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.BookmarkPolicyFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.CssClassFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.FaFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.ObjectDescribedFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.ObjectNamedFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.domainobjectlayout.tabledec.TableDecoratorFacetForDomainObjectLayoutXml; +import org.apache.causeway.core.metamodel.facets.object.promptStyle.PromptStyleFacet; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.CssClassFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.HiddenFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.LabelAtFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MemberDescribedFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MemberNamedFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.MultiLineFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.RenderedAdjustedFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.TypicalLengthFacetForPropertyLayoutXml; +import org.apache.causeway.core.metamodel.facets.properties.propertylayout.UnchangingFacetForPropertyLayoutXml; import org.apache.causeway.core.metamodel.layout.LayoutFacetUtil.LayoutDataFactory; -import org.apache.causeway.core.metamodel.services.grid.GridSystemServiceAbstract; +import org.apache.causeway.core.metamodel.layout.LayoutFacetUtil.MetamodelToGridOverridingVisitor; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.feature.MixedIn; import org.apache.causeway.core.metamodel.spec.feature.ObjectAction; @@ -82,6 +121,8 @@ import org.apache.causeway.core.metamodel.specloader.SpecificationLoader; import static org.apache.causeway.commons.internal.base._NullSafe.stream; +import static org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacet; +import static org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacetIfPresent; import lombok.Setter; import lombok.experimental.Accessors; @@ -99,7 +140,7 @@ @Qualifier("Bootstrap") @Slf4j public class GridSystemServiceBootstrap -extends GridSystemServiceAbstract { +implements GridSystemService { /** * SPI to customize layout fallback behavior on a per class basis. @@ -116,6 +157,9 @@ public static interface FallbackLayoutDataSource { @Setter @Accessors(chain = true) // JUnit support private GridMarshaller marshaller; + private final Provider<SpecificationLoader> specLoaderProvider; + private final MessageService messageService; + private final CausewaySystemEnvironment causewaySystemEnvironment; private final CausewayConfiguration config; private final Can<FallbackLayoutDataSource> fallbackLayoutDataSources; @@ -128,18 +172,14 @@ public GridSystemServiceBootstrap( final MessageService messageService, final CausewaySystemEnvironment causewaySystemEnvironment, final List<FallbackLayoutDataSource> fallbackLayoutDataSources) { - super(specLoaderProvider, translationService, messageService, causewaySystemEnvironment); + this.specLoaderProvider = specLoaderProvider; + this.messageService = messageService; + this.causewaySystemEnvironment = causewaySystemEnvironment; this.config = causewayConfiguration; this.fallbackLayoutDataSources = Can.ofCollection(fallbackLayoutDataSources); } - @Override - public Class<BSGrid> gridImplementation() { - return BSGrid.class; - } - - @Override - protected String toXml(final BSGrid grid) { + private String toXml(final BSGrid grid) { return marshaller.marshal(grid, CommonMimeType.XML); } @@ -224,8 +264,12 @@ static void addFieldSetsToColumn( } } - @Override - protected boolean validateAndNormalize( + /** + * Mandatory hook method for subclasses, where they must ensure that all object members (properties, collections + * and actions) are in the grid metadata, typically by deriving this information from other existing metadata + * (eg facets from annotations) or just by applying default rules. + */ + private boolean validateAndNormalize( final BSGrid bsGrid, final Class<?> domainClass) { @@ -610,4 +654,294 @@ private void addActionTo( actionLayoutData.owner(owner); } + @Override + public void normalize(final BSGrid grid, final Class<?> domainClass) { + final boolean valid = validateAndNormalize(grid, domainClass); + if (valid) { + overwriteFacets(grid, domainClass); + if(log.isDebugEnabled()) { + log.debug("Grid:\n\n{}\n\n", toXml(grid)); + } + } else { + if(causewaySystemEnvironment.isPrototyping()) { + messageService.warnUser("Grid metadata errors for " + grid.domainClass().getName() + "; check the error log"); + } + log.error("Grid metadata errors in {}:\n\n{}\n\n", grid.domainClass().getName(), toXml(grid)); + } + } + + /** + * Overwrites (replaces) any existing facets in the metamodel with info taken from the grid. + * + * @implNote This code uses {@link FacetUtil#updateFacet(Facet)} + * because the layout might be reloaded from XML if reloading is supported. + */ + private void overwriteFacets( + final BSGrid fcGrid, + final Class<?> domainClass) { + + var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); + + var oneToOneAssociationById = ObjectMember.mapById(objectSpec.streamProperties(MixedIn.INCLUDED)); + var oneToManyAssociationById = ObjectMember.mapById(objectSpec.streamCollections(MixedIn.INCLUDED)); + var objectActionById = ObjectMember.mapById(objectSpec.streamRuntimeActions(MixedIn.INCLUDED)); + + // governs, whether annotations win over XML grid, based on whether XML grid is fallback or 'explicit' + var precedence = fcGrid.isFallback() + ? Facet.Precedence.LOW // fallback case: XML layout is overruled by layout from annotations + : Facet.Precedence.HIGH; // non-fallback case: XML layout overrules layout from annotations + + final AtomicInteger propertySequence = new AtomicInteger(0); + fcGrid.visit(new BSElementVisitor() { + private int collectionSequence = 1; + + private int actionDomainObjectSequence = 1; + private int actionPropertyGroupSequence = 1; + private int actionPropertySequence = 1; + private int actionCollectionSequence = 1; + + @Override + public void visit(final DomainObjectLayoutData domainObjectLayoutData) { + + updateFacetIfPresent( + BookmarkPolicyFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + updateFacetIfPresent( + CssClassFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + updateFacetIfPresent( + FaFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + updateFacetIfPresent( + ObjectDescribedFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + updateFacetIfPresent( + ObjectNamedFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + updateFacetIfPresent( + TableDecoratorFacetForDomainObjectLayoutXml + .create(domainObjectLayoutData, objectSpec, precedence)); + } + + @Override + public void visit(final ActionLayoutData actionLayoutData) { + + var actionLayoutDataOwner = actionLayoutData.owner(); + var objectAction = objectActionById.get(actionLayoutData.getId()); + if(objectAction == null) return; + + { + GroupIdAndName groupIdAndName = null; + int memberOrderSequence; + if(actionLayoutDataOwner instanceof FieldSet) { + var fieldSet = (FieldSet) actionLayoutDataOwner; + for (var propertyLayoutData : fieldSet.getProperties()) { + // any will do; choose the first one that we know is valid + if(oneToOneAssociationById.containsKey(propertyLayoutData.getId())) { + groupIdAndName = GroupIdAndName + .forPropertyLayoutData(propertyLayoutData) + .orElse(null); + break; + } + } + memberOrderSequence = actionPropertyGroupSequence++; + } else if(actionLayoutDataOwner instanceof PropertyLayoutData) { + groupIdAndName = GroupIdAndName + .forPropertyLayoutData((PropertyLayoutData) actionLayoutDataOwner) + .orElse(null); + memberOrderSequence = actionPropertySequence++; + } else if(actionLayoutDataOwner instanceof CollectionLayoutData) { + groupIdAndName = GroupIdAndName + .forCollectionLayoutData((CollectionLayoutData) actionLayoutDataOwner) + .orElse(null); + memberOrderSequence = actionCollectionSequence++; + } else { + // don't add: any existing metadata should be preserved + groupIdAndName = null; + memberOrderSequence = actionDomainObjectSequence++; + } + updateFacet( + LayoutOrderFacetForLayoutXml.create(memberOrderSequence, objectAction, precedence)); + + //XXX hotfix: always override LayoutGroupFacetFromActionLayoutAnnotation, otherwise actions are not shown - don't know why + var precedenceHotfix = fcGrid.isFallback() + ? Facet.Precedence.DEFAULT + : Facet.Precedence.HIGH; + + updateFacetIfPresent( + LayoutGroupFacetForLayoutXml.create(groupIdAndName, objectAction, precedenceHotfix)); + } + + // fix up the action position if required + if(actionLayoutDataOwner instanceof FieldSet) { + if(actionLayoutData.getPosition() == null || + actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.BELOW || + actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.RIGHT) { + actionLayoutData.setPosition(org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL); + } + } else if(actionLayoutDataOwner instanceof PropertyLayoutData) { + if(actionLayoutData.getPosition() == null || + actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL_DROPDOWN || + actionLayoutData.getPosition() == org.apache.causeway.applib.annotation.ActionLayout.Position.PANEL) { + actionLayoutData.setPosition(org.apache.causeway.applib.annotation.ActionLayout.Position.BELOW); + } + } else { + // doesn't do anything for DomainObject or Collection + actionLayoutData.setPosition(null); + } + + updateFacetIfPresent( + ActionPositionFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + CssClassFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + FaFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + MemberDescribedFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + HiddenFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + MemberNamedFacetForActionLayoutXml.create(actionLayoutData, objectAction, precedence)); + + updateFacetIfPresent( + Optional.ofNullable(actionLayoutData) + .map(ActionLayoutData::getPromptStyle) + .map(promptStyle->new PromptStyleFacet("ActionLayoutXml", promptStyle, objectAction, precedence, true))); + + } + + @Override + public void visit(final PropertyLayoutData propertyLayoutData) { + var oneToOneAssociation = oneToOneAssociationById.get(propertyLayoutData.getId()); + if(oneToOneAssociation == null) return; + + updateFacetIfPresent( + CssClassFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + MemberDescribedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + HiddenFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + LabelAtFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + MultiLineFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + MemberNamedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + Optional.ofNullable(propertyLayoutData) + .map(PropertyLayoutData::getPromptStyle) + .map(promptStyle->new PromptStyleFacet("PropertyLayoutXml", promptStyle, oneToOneAssociation, precedence, true))); + + updateFacetIfPresent( + RenderedAdjustedFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + UnchangingFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + updateFacetIfPresent( + TypicalLengthFacetForPropertyLayoutXml.create(propertyLayoutData, oneToOneAssociation, precedence)); + + // Layout group-name based on owning property group, Layout sequence monotonically increasing + // nb for any given field set the sequence won't reset to zero; however this is what we want so that + // table columns are shown correctly (by fieldset, then property order within that fieldset). + final FieldSet fieldSet = propertyLayoutData.owner(); + + updateFacet(LayoutOrderFacetForLayoutXml.create(propertySequence.incrementAndGet(), oneToOneAssociation, precedence)); + + updateFacetIfPresent( + LayoutGroupFacetForLayoutXml.create(fieldSet, oneToOneAssociation, precedence)); + } + + @Override + public void visit(final CollectionLayoutData collectionLayoutData) { + var oneToManyAssociation = oneToManyAssociationById.get(collectionLayoutData.getId()); + if(oneToManyAssociation == null) { + return; + } + + updateFacetIfPresent( + CssClassFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + DefaultViewFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + TableDecoratorFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + MemberDescribedFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + HiddenFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + MemberNamedFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + PagedFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacetIfPresent( + SortedByFacetForCollectionLayoutXml + .create(collectionLayoutData, oneToManyAssociation, precedence)); + + updateFacet(LayoutOrderFacetForLayoutXml + .create(collectionSequence++, oneToManyAssociation, precedence)); + } + }); + } + + // -- + + @Override + public void complete(final BSGrid grid, final Class<?> domainClass) { + normalize(grid, domainClass); + var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); + grid.visit(new MetamodelToGridOverridingVisitor(objectSpec)); + } + + @Override + public void minimal(final BSGrid grid, final Class<?> domainClass) { + normalize(grid, domainClass); + grid.visit(new BSElementVisitor() { + @Override + public void visit(final ActionLayoutData actionLayoutData) { + actionLayoutData.owner().getActions().remove(actionLayoutData); + } + + @Override + public void visit(final CollectionLayoutData collectionLayoutData) { + collectionLayoutData.owner().getCollections().remove(collectionLayoutData); + } + + @Override + public void visit(final PropertyLayoutData propertyLayoutData) { + propertyLayoutData.owner().getProperties().remove(propertyLayoutData); + } + + @Override + public void visit(final DomainObjectLayoutData domainObjectLayoutData) { + final DomainObjectLayoutDataOwner owner = domainObjectLayoutData.owner(); + owner.setDomainObject(new DomainObjectLayoutData()); + } + }); + } + } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java index fe18cc31c36..be3b4352239 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java @@ -23,7 +23,7 @@ import jakarta.xml.bind.Marshaller; -public record XsiSchemaLocationProviderForGrid() { +record XsiSchemaLocationProviderForGrid() { static final String COMPONENT_TNS = "https://causeway.apache.org/applib/layout/component"; static final String COMPONENT_SCHEMA_LOCATION = "https://causeway.apache.org/applib/layout/component/component.xsd"; diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_UnreferencedSequenceUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/_UnreferencedSequenceUtil.java similarity index 97% rename from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_UnreferencedSequenceUtil.java rename to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/_UnreferencedSequenceUtil.java index 937771e64da..90d8c816ded 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_UnreferencedSequenceUtil.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/_UnreferencedSequenceUtil.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.causeway.core.metamodel.services.grid.bootstrap; +package org.apache.causeway.core.metamodel.services.grid; import java.util.List; import java.util.stream.Collectors; diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResource.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResource.java index 26d0d33c103..82c8aff25b7 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResource.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResource.java @@ -20,11 +20,11 @@ import org.jspecify.annotations.NonNull; -import org.apache.causeway.applib.value.NamedWithMimeType; +import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; public record LayoutResource( @NonNull String resourceName, - NamedWithMimeType.@NonNull CommonMimeType format, + @NonNull CommonMimeType format, @NonNull String content) { } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoader.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoader.java index a3edd90dee1..7e280f337ba 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoader.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoader.java @@ -20,14 +20,12 @@ import java.util.Optional; -import org.apache.causeway.applib.annotation.Programmatic; -import org.apache.causeway.commons.functional.Try; -import org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault; - import org.jspecify.annotations.NonNull; +import org.apache.causeway.commons.functional.Try; + /** - * A simpler SPI for {@link GridLoaderServiceDefault}. + * SPI for grid loading. * * @since 2.0 {@index} */ @@ -36,7 +34,6 @@ public interface LayoutResourceLoader { /** * Try to locate and load a {@link LayoutResource} by type and name. */ - @Programmatic Try<LayoutResource> tryLoadLayoutResource( final @NonNull Class<?> type, final @NonNull String candidateResourceName); @@ -45,10 +42,9 @@ Try<LayoutResource> tryLoadLayoutResource( * Optionally returns a {@link LayoutResource} based * on whether it could be resolved by type and name * and successfully read. - * <p> - * Silently ignores exceptions underneath, if any. + * + * <p>Silently ignores exceptions underneath, if any. */ - @Programmatic default Optional<LayoutResource> lookupLayoutResource( final @NonNull Class<?> type, final @NonNull String candidateResourceName) { diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoaderDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoaderDefault.java index 606d77367f9..ea5e29c4689 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoaderDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/spi/LayoutResourceLoaderDefault.java @@ -21,6 +21,8 @@ import jakarta.annotation.Priority; import jakarta.inject.Named; +import org.jspecify.annotations.NonNull; + import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @@ -29,8 +31,6 @@ import org.apache.causeway.commons.functional.Try; import org.apache.causeway.commons.io.DataSource; import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel; -import org.jspecify.annotations.NonNull; -import lombok.RequiredArgsConstructor; /** * Default implementation of {@link LayoutResourceLoader}. @@ -41,9 +41,8 @@ @Named(CausewayModuleCoreMetamodel.NAMESPACE + ".LayoutResourceLoaderDefault") @Priority(PriorityPrecedence.MIDPOINT) @Qualifier("Default") -@RequiredArgsConstructor //JUnit Support //@Slf4j -public class LayoutResourceLoaderDefault implements LayoutResourceLoader { +public record LayoutResourceLoaderDefault() implements LayoutResourceLoader { @Override public Try<LayoutResource> tryLoadLayoutResource( diff --git a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java similarity index 72% rename from core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java rename to core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java index 97ef3caa513..dc64a762030 100644 --- a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault_resourceNameTest.java +++ b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java @@ -19,6 +19,7 @@ package org.apache.causeway.core.metamodel.services.grid; import java.util.EnumSet; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,57 +27,56 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; -import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault.LayoutKey; +import org.apache.causeway.core.metamodel.services.grid.GridLoader.LayoutKey; import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResource; import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader; import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault; -class GridLoaderServiceDefault_resourceNameTest { +class GridCache_resourceNameTest { - private GridLoaderServiceDefault gridLoaderServiceDefault; + private GridCache gridCache; private LayoutResourceLoader layoutResourceLoader; @BeforeEach void setUp() throws Exception { layoutResourceLoader = new LayoutResourceLoaderDefault(); - gridLoaderServiceDefault = new GridLoaderServiceDefault(null, Can.of(layoutResourceLoader), false); + gridCache = new GridCache(null, false, List.of(layoutResourceLoader)); } @Test void when_default_exists() { assertEquals( "Foo.layout.xml", - resourceNameFor(new GridLoaderServiceDefault.LayoutKey(Foo.class, null))); + resourceNameFor(new GridLoader.LayoutKey(Foo.class, null))); } @Test void when_fallback_exists() { assertEquals( "Foo2.layout.fallback.xml", - resourceNameFor(new GridLoaderServiceDefault.LayoutKey(Foo2.class, null))); + resourceNameFor(new GridLoader.LayoutKey(Foo2.class, null))); } @Test void when_default_and_fallback_both_exist() { assertEquals( "Foo3.layout.xml", - resourceNameFor(new GridLoaderServiceDefault.LayoutKey(Foo3.class, null))); + resourceNameFor(new GridLoader.LayoutKey(Foo3.class, null))); } @Test void when_neither_exist() { assertEquals( (String)null, - resourceNameFor(new GridLoaderServiceDefault.LayoutKey(Foo4.class, null))); + resourceNameFor(new GridLoader.LayoutKey(Foo4.class, null))); } // -- HELPER private String resourceNameFor(final LayoutKey dcal) { - return gridLoaderServiceDefault.loadLayoutResource(dcal, EnumSet.of(CommonMimeType.XML)) - .map(LayoutResource::resourceName) - .orElse(null); + return gridCache.gridLoader().loadLayoutResource(dcal, EnumSet.of(CommonMimeType.XML)) + .map(LayoutResource::resourceName) + .orElse(null); } } diff --git a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java index fbf06ae92fe..aeda1067fa7 100644 --- a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java +++ b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingTest.java @@ -26,34 +26,49 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.apache.causeway.applib.services.grid.GridLoaderService; +import org.apache.causeway.applib.services.grid.GridService; import org.apache.causeway.applib.services.layout.LayoutExportStyle; import org.apache.causeway.applib.services.layout.LayoutService; import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; +import org.apache.causeway.core.config.environment.CausewaySystemEnvironment; import org.apache.causeway.core.metamodel.MetaModelTestAbstract; import org.apache.causeway.core.metamodel.facetapi.Facet.Precedence; import org.apache.causeway.core.metamodel.facets.all.named.MemberNamedFacet; import org.apache.causeway.core.metamodel.facets.object.grid.GridFacet; import org.apache.causeway.core.metamodel.object.ManagedObject; +import org.apache.causeway.core.mmtestsupport.MetaModelContext_forTesting.MetaModelContext_forTestingBuilder; class GridLoadingTest extends MetaModelTestAbstract { - private GridLoaderServiceDefault gridLoaderService; + private GridCache gridCache; private LayoutService layoutService; + @Override + protected void onSetUp(final MetaModelContext_forTestingBuilder mmcBuilder) { + CausewaySystemEnvironment.setPrototyping(true); + var env = new CausewaySystemEnvironment(); + CausewaySystemEnvironment.setPrototyping(false); + assertTrue(env.isPrototyping()); + mmcBuilder.systemEnvironment(env); + super.onSetUp(mmcBuilder); + } + @Override protected void afterSetUp() { layoutService = getServiceRegistry().lookupServiceElseFail(LayoutService.class); - gridLoaderService = (GridLoaderServiceDefault)getServiceRegistry() - .lookupServiceElseFail(GridLoaderService.class); + gridCache = ((GridServiceDefault) getServiceRegistry() + .lookupServiceElseFail(GridService.class)) + .gridCache(); + assertTrue(gridCache.supportsReloading()); } // test blueprint, for future work void blueprint() { - var domainClassAndLayout = new GridLoaderServiceDefault.LayoutKey(Bar.class, null); - gridLoaderService.loadLayoutResource(domainClassAndLayout, EnumSet.of(CommonMimeType.XML)); + var domainClassAndLayout = new GridLoader.LayoutKey(Bar.class, null); + gridCache.gridLoader().loadLayoutResource(domainClassAndLayout, EnumSet.of(CommonMimeType.XML)); var xml = layoutService.objectLayout(Bar.class, LayoutExportStyle.MINIMAL, CommonMimeType.XML); System.out.println(xml); diff --git a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java index 71af4f7c29d..ed22ea59e17 100644 --- a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java +++ b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java @@ -35,7 +35,6 @@ import org.springframework.util.ClassUtils; import org.apache.causeway.applib.services.factory.FactoryService; -import org.apache.causeway.applib.services.grid.GridLoaderService; import org.apache.causeway.applib.services.grid.GridMarshaller; import org.apache.causeway.applib.services.grid.GridService; import org.apache.causeway.applib.services.grid.GridSystemService; @@ -90,11 +89,9 @@ import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry; import org.apache.causeway.core.metamodel.services.command.CommandDtoFactory; import org.apache.causeway.core.metamodel.services.events.MetamodelEventService; -import org.apache.causeway.core.metamodel.services.grid.GridLoaderServiceDefault; +import org.apache.causeway.core.metamodel.services.grid.GridMarshallerXml; import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault; -import org.apache.causeway.core.metamodel.services.grid.XsiSchemaLocationProviderForGrid; -import org.apache.causeway.core.metamodel.services.grid.bootstrap.GridMarshallerServiceBootstrap; -import org.apache.causeway.core.metamodel.services.grid.bootstrap.GridSystemServiceBootstrap; +import org.apache.causeway.core.metamodel.services.grid.GridSystemServiceBootstrap; import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault; import org.apache.causeway.core.metamodel.services.layout.LayoutServiceDefault; import org.apache.causeway.core.metamodel.services.message.MessageServiceNoop; @@ -300,7 +297,6 @@ Stream<SingletonBeanProvider> streamBeanAdapters() { discoveredServices.stream(), Stream.of( // support for lazy bean providers, - SingletonBeanProvider.forTestingLazy(GridLoaderService.class, this::getGridLoaderService), SingletonBeanProvider.forTestingLazy(GridService.class, this::getGridService), SingletonBeanProvider.forTestingLazy(JaxbService.class, this::getJaxbService), SingletonBeanProvider.forTestingLazy(MenuBarsService.class, this::getMenuBarsService), @@ -458,7 +454,7 @@ private final MenuBarsService createMenuBarsService() { @Getter(lazy = true) private final GridMarshaller gridMarshaller = createGridMarshaller(); private final GridMarshaller createGridMarshaller() { - return new GridMarshallerServiceBootstrap(getJaxbService(), new XsiSchemaLocationProviderForGrid()); + return new GridMarshallerXml(getJaxbService()); } @Getter(lazy = true) @@ -476,19 +472,15 @@ private final List<GridSystemService> createGridSystemService() { .setMarshaller(getGridMarshaller())); } - @Getter(lazy = true) - private final GridLoaderService gridLoaderService = createGridLoaderService(); - private final GridLoaderService createGridLoaderService() { - return new GridLoaderServiceDefault(getMessageService(), Can.of(new LayoutResourceLoaderDefault()), /*support reloading*/true); - } - @Getter(lazy = true) private final GridService gridService = createGridService(); private final GridService createGridService() { return new GridServiceDefault( - getGridLoaderService(), + getSystemEnvironment(), getGridMarshaller(), - getGridSystemServices()); + getMessageService(), + getGridSystemServices(), + List.of(new LayoutResourceLoaderDefault())); } @Getter(lazy = true)
