001// Generated by delombok at Mon Oct 02 22:22:47 BST 2023
002/*
003 *  Licensed to the Apache Software Foundation (ASF) under one
004 *  or more contributor license agreements.  See the NOTICE file
005 *  distributed with this work for additional information
006 *  regarding copyright ownership.  The ASF licenses this file
007 *  to you under the Apache License, Version 2.0 (the
008 *  "License"); you may not use this file except in compliance
009 *  with the License.  You may obtain a copy of the License at
010 *
011 *        http://www.apache.org/licenses/LICENSE-2.0
012 *
013 *  Unless required by applicable law or agreed to in writing,
014 *  software distributed under the License is distributed on an
015 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 *  KIND, either express or implied.  See the License for the
017 *  specific language governing permissions and limitations
018 *  under the License.
019 */
020package org.apache.causeway.extensions.excel.testing;
021
022import java.io.InputStream;
023import java.net.URL;
024import java.util.*;
025import javax.inject.Inject;
026import javax.inject.Named;
027import org.apache.causeway.applib.annotation.DomainObject;
028import org.apache.causeway.applib.annotation.Programmatic;
029import org.apache.causeway.applib.annotation.PropertyLayout;
030import org.apache.causeway.applib.services.metamodel.BeanSort;
031import org.apache.causeway.applib.services.repository.RepositoryService;
032import org.apache.causeway.applib.value.Blob;
033import org.apache.causeway.commons.internal.base._Bytes;
034import org.apache.causeway.commons.internal.base._NullSafe;
035import org.apache.causeway.commons.internal.collections._Lists;
036import org.apache.causeway.commons.internal.collections._Maps;
037import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
038import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
039import org.apache.causeway.extensions.excel.applib.CausewayModuleExtExcelApplib;
040import org.apache.causeway.extensions.excel.applib.ExcelService;
041import org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureResultList;
042import org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScript;
043import org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScriptWithExecutionStrategy;
044import org.apache.causeway.testing.fixtures.applib.fixturescripts.FixtureScripts;
045
046/**
047 * @since 2.0 {@index}
048 */
049@Named(ExcelFixture.LOGICAL_TYPE_NAME)
050@DomainObject
051public class ExcelFixture extends FixtureScript implements FixtureScriptWithExecutionStrategy {
052    public static final String LOGICAL_TYPE_NAME = CausewayModuleExtExcelApplib.NAMESPACE + ".ExcelFixture";
053    private final List<Class<?>> classes;
054    @Inject
055    SpecificationLoader specLoader;
056    @Inject
057    ExcelService excelService;
058    @Inject
059    RepositoryService repositoryService;
060
061    public ExcelFixture(final URL excelResource, final Class<?>... classes) {
062        this(excelResource, Arrays.asList(classes));
063    }
064
065    public ExcelFixture(final URL excelResource, final List<Class<?>> classes) {
066        this(classes);
067        setExcelResource(excelResource);
068    }
069
070    public ExcelFixture(final Blob upload, final Class<?>... classes) {
071        this(upload, Arrays.asList(classes));
072    }
073
074    public ExcelFixture(final Blob blob, final List<Class<?>> classes) {
075        this(classes);
076        setBlob(blob);
077    }
078
079    private ExcelFixture(final List<Class<?>> classes) {
080        for (Class<?> cls : classes) {
081            final org.apache.causeway.applib.services.metamodel.BeanSort beanSort = Optional.ofNullable(specLoader).flatMap(sl -> sl.specForType(cls)).filter(_NullSafe::isPresent).map(ObjectSpecification::getBeanSort).orElse(BeanSort.UNKNOWN);
082            if (!beanSort.isViewModel() && !beanSort.isEntity()) {
083                throw new IllegalArgumentException(String.format("Class \'%s\' does not implement \'%s\', nor is it persistable", cls.getSimpleName(), ExcelFixtureRowHandler.class.getSimpleName()));
084            }
085        }
086        this.classes = classes;
087    }
088
089    /**
090     * Input, optional: defines the name of the resource, used as a suffix to override {@link #getQualifiedName()}
091     * (disambiguate items when added to {@link FixtureResultList} if multiple instances of {@link ExcelFixture} are
092     * used with different excel spreadsheets).
093     */
094    @PropertyLayout(sequence = "1.1")
095    private String excelResourceName;
096
097    @Programmatic
098    @Override
099    public String getQualifiedName() {
100        return super.getQualifiedName() + (getExcelResourceName() != null ? "-" + getExcelResourceName() : "");
101    }
102
103    /**
104     * Input, mandatory ... the Excel spreadsheet to read.
105     */
106    @PropertyLayout(sequence = "1.2")
107    private URL excelResource;
108    /**
109     * Input, mandatory ... the Excel spreadsheet to read.
110     */
111    private Blob blob;
112    /**
113     * Output: all the objects created by this fixture.
114     */
115    private final List objects = _Lists.newArrayList();
116    /**
117     * Output: the objects created by this fixture, for a specific persistable/row handler class.
118     */
119    private final Map<Class<?>, List<Object>> objectsByClass = _Maps.newHashMap();
120
121    @Override
122    protected void execute(final ExecutionContext ec) {
123        if (blob == null) {
124            byte[] bytes = getBytes();
125            blob = new Blob("unused", ExcelService.XSLX_MIME_TYPE, bytes);
126        }
127        for (Class<?> cls : classes) {
128            final List<?> rowObjects = excelService.fromExcel(blob, cls, cls.getSimpleName());
129            Object previousRow = null;
130            for (final Object rowObj : rowObjects) {
131                final List<Object> createdObjects = create(rowObj, ec, previousRow);
132                if (createdObjects != null) {
133                    addToMap(cls, createdObjects);
134                    addToCombined(createdObjects);
135                }
136                previousRow = rowObj;
137            }
138        }
139    }
140
141    private List<Object> create(final Object rowObj, final ExecutionContext ec, final Object previousRow) {
142        if (rowObj instanceof ExcelFixtureRowHandler) {
143            final ExcelFixtureRowHandler rowHandler = (ExcelFixtureRowHandler) rowObj;
144            return rowHandler.handleRow(ec, this, previousRow);
145        } else {
146            repositoryService.persist(rowObj);
147            ec.addResult(this, rowObj);
148            return Collections.singletonList(rowObj);
149        }
150    }
151
152    private void addToMap(final Class<?> cls, final List<Object> createdObjects) {
153        List<Object> objectList = objectsByClass.get(cls);
154        if (objectList == null) {
155            objectList = _Lists.newArrayList();
156            this.objectsByClass.put(cls, objectList);
157        }
158        objectList.addAll(createdObjects);
159    }
160
161    private void addToCombined(final List<Object> createdObjects) {
162        this.objects.addAll(createdObjects);
163    }
164
165    private byte[] bytes;
166
167    private byte[] getBytes() {
168        if (bytes == null) {
169            if (blob != null) {
170                bytes = blob.getBytes();
171            } else {
172                bytes = readBytes();
173            }
174        }
175        return bytes;
176    }
177
178    private byte[] readBytes() {
179        try (InputStream is = getExcelResource().openStream()) {
180            return _Bytes.of(is);
181        } catch (Exception e) {
182            throw new IllegalArgumentException("Could not read from resource: " + getExcelResource());
183        }
184    }
185
186    @Override
187    public FixtureScripts.MultipleExecutionStrategy getMultipleExecutionStrategy() {
188        return FixtureScripts.MultipleExecutionStrategy.EXECUTE_ONCE_BY_VALUE;
189    }
190
191    @Override
192    public boolean equals(final Object o) {
193        if (this == o) return true;
194        if (o == null || getClass() != o.getClass()) return false;
195        final ExcelFixture that = (ExcelFixture) o;
196        return Arrays.equals(getBytes(), that.getBytes());
197    }
198
199    @Override
200    public int hashCode() {
201        return Arrays.hashCode(getBytes());
202    }
203
204    /**
205     * Input, optional: defines the name of the resource, used as a suffix to override {@link #getQualifiedName()}
206     * (disambiguate items when added to {@link FixtureResultList} if multiple instances of {@link ExcelFixture} are
207     * used with different excel spreadsheets).
208     */
209    @java.lang.SuppressWarnings("all")
210    public String getExcelResourceName() {
211        return this.excelResourceName;
212    }
213
214    /**
215     * Input, optional: defines the name of the resource, used as a suffix to override {@link #getQualifiedName()}
216     * (disambiguate items when added to {@link FixtureResultList} if multiple instances of {@link ExcelFixture} are
217     * used with different excel spreadsheets).
218     */
219    @java.lang.SuppressWarnings("all")
220    public void setExcelResourceName(final String excelResourceName) {
221        this.excelResourceName = excelResourceName;
222    }
223
224    /**
225     * Input, mandatory ... the Excel spreadsheet to read.
226     */
227    @java.lang.SuppressWarnings("all")
228    public URL getExcelResource() {
229        return this.excelResource;
230    }
231
232    /**
233     * Input, mandatory ... the Excel spreadsheet to read.
234     */
235    @java.lang.SuppressWarnings("all")
236    public void setExcelResource(final URL excelResource) {
237        this.excelResource = excelResource;
238    }
239
240    /**
241     * Input, mandatory ... the Excel spreadsheet to read.
242     */
243    @java.lang.SuppressWarnings("all")
244    public Blob getBlob() {
245        return this.blob;
246    }
247
248    /**
249     * Input, mandatory ... the Excel spreadsheet to read.
250     */
251    @java.lang.SuppressWarnings("all")
252    public void setBlob(final Blob blob) {
253        this.blob = blob;
254    }
255
256    /**
257     * Output: all the objects created by this fixture.
258     */
259    @java.lang.SuppressWarnings("all")
260    public List getObjects() {
261        return this.objects;
262    }
263
264    /**
265     * Output: the objects created by this fixture, for a specific persistable/row handler class.
266     */
267    @java.lang.SuppressWarnings("all")
268    public Map<Class<?>, List<Object>> getObjectsByClass() {
269        return this.objectsByClass;
270    }
271}