001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.core.metamodel.services.devutils;
020
021import java.io.ByteArrayOutputStream;
022import java.io.File;
023import java.io.IOException;
024import java.io.OutputStreamWriter;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.List;
028import java.util.zip.ZipEntry;
029import java.util.zip.ZipOutputStream;
030import javax.activation.MimeType;
031import javax.activation.MimeTypeParseException;
032import com.google.common.base.Predicate;
033import com.google.common.collect.Collections2;
034import com.google.common.collect.Lists;
035import org.apache.isis.applib.FatalException;
036import org.apache.isis.applib.annotation.Programmatic;
037import org.apache.isis.applib.services.devutils.DeveloperUtilitiesService;
038import org.apache.isis.applib.value.Blob;
039import org.apache.isis.applib.value.Clob;
040import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
041import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
042import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
043import org.apache.isis.core.metamodel.layoutmetadata.json.LayoutMetadataReaderFromJson;
044import org.apache.isis.core.metamodel.spec.ObjectSpecification;
045import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
046import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpiAware;
047import org.apache.isis.core.metamodel.spec.feature.*;
048
049public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesService, SpecificationLoaderSpiAware, AdapterManagerAware {
050
051
052    private final MimeType mimeTypeTextCsv;
053    private final MimeType mimeTypeApplicationZip;
054    private final MimeType mimeTypeApplicationJson;
055
056    public DeveloperUtilitiesServiceDefault() {
057        try {
058            mimeTypeTextCsv = new MimeType("text", "csv");
059            mimeTypeApplicationJson = new MimeType("application", "jzon");
060            mimeTypeApplicationZip = new MimeType("application", "zip");
061        } catch (MimeTypeParseException e) {
062            throw new RuntimeException(e);
063        }
064    }
065
066    // //////////////////////////////////////
067
068    
069    @Override
070    public Clob downloadMetaModel() {
071
072        final Collection<ObjectSpecification> specifications = specificationLoader.allSpecifications();
073
074        final List<MetaModelRow> rows = Lists.newArrayList();
075        for (ObjectSpecification spec : specifications) {
076            if (exclude(spec)) {
077                continue;
078            }
079            final List<ObjectAssociation> properties = spec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES);
080            for (ObjectAssociation property : properties) {
081                final OneToOneAssociation otoa = (OneToOneAssociation) property;
082                if (exclude(otoa)) {
083                    continue;
084                }
085                rows.add(new MetaModelRow(spec, otoa));
086            }
087            final List<ObjectAssociation> associations = spec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.COLLECTIONS);
088            for (ObjectAssociation collection : associations) {
089                final OneToManyAssociation otma = (OneToManyAssociation) collection;
090                if (exclude(otma)) {
091                    continue;
092                }
093                rows.add(new MetaModelRow(spec, otma));
094            }
095            final List<ObjectAction> actions = spec.getObjectActions(Contributed.INCLUDED);
096            for (ObjectAction action : actions) {
097                if (exclude(action)) {
098                    continue;
099                }
100                rows.add(new MetaModelRow(spec, action));
101            }
102        }
103
104        Collections.sort(rows);
105
106        final StringBuilder buf = new StringBuilder();
107        buf.append(MetaModelRow.header()).append("\n");
108        for (MetaModelRow row : rows) {
109            buf.append(row.asTextCsv()).append("\n");
110        }
111        return new Clob("metamodel.csv", mimeTypeTextCsv, buf.toString().toCharArray());
112    }
113
114    protected boolean exclude(OneToOneAssociation property) {
115        return false;
116    }
117
118    protected boolean exclude(OneToManyAssociation collection) {
119        return false;
120    }
121
122    protected boolean exclude(ObjectAction action) {
123        return false;
124    }
125
126    protected boolean exclude(ObjectSpecification spec) {
127        return isBuiltIn(spec) || spec.isAbstract();
128    }
129
130    protected boolean isBuiltIn(ObjectSpecification spec) {
131        final String className = spec.getFullIdentifier();
132        return className.startsWith("java") || className.startsWith("org.joda");
133    }
134
135    // //////////////////////////////////////
136    
137    @Override
138    public void refreshServices() {
139        Collection<ObjectSpecification> specifications = Lists.newArrayList(specificationLoader.allSpecifications());
140        for (ObjectSpecification objectSpec : specifications) {
141            if(objectSpec.isService()){
142                specificationLoader.invalidateCache(objectSpec.getCorrespondingClass());
143            }
144        }
145    }
146
147    // //////////////////////////////////////
148
149    @Override
150    public Object refreshLayout(Object domainObject) {
151        specificationLoader.invalidateCacheFor(domainObject);
152        return domainObject;
153    }
154
155    // //////////////////////////////////////
156    
157    @Override
158    public Clob downloadLayout(Object domainObject) {
159        
160        final ObjectAdapter adapterFor = adapterManager.adapterFor(domainObject);
161        final ObjectSpecification objectSpec = adapterFor.getSpecification();
162        
163        final LayoutMetadataReaderFromJson propertiesReader = new LayoutMetadataReaderFromJson();
164        final String json = propertiesReader.asJson(objectSpec);
165        
166        return new Clob(objectSpec.getShortIdentifier() +".layout.json", mimeTypeApplicationJson, json);
167    }
168
169    // //////////////////////////////////////
170
171    @Override
172    public Blob downloadLayouts() {
173        final LayoutMetadataReaderFromJson propertiesReader = new LayoutMetadataReaderFromJson();
174        final Collection<ObjectSpecification> allSpecs = specificationLoader.allSpecifications();
175        final Collection<ObjectSpecification> domainObjectSpecs = Collections2.filter(allSpecs, new Predicate<ObjectSpecification>(){
176            @Override
177            public boolean apply(ObjectSpecification input) {
178                return  !input.isAbstract() && 
179                        !input.isService() && 
180                        !input.isValue() && 
181                        !input.isParentedOrFreeCollection();
182            }});
183        try {
184            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
185            ZipOutputStream zos = new ZipOutputStream(baos);
186            OutputStreamWriter writer = new OutputStreamWriter(zos);
187            for (ObjectSpecification objectSpec : domainObjectSpecs) {
188                zos.putNextEntry(new ZipEntry(zipEntryNameFor(objectSpec)));
189                writer.write(propertiesReader.asJson(objectSpec));
190                writer.flush();
191                zos.closeEntry();
192            }
193            writer.close();
194            return new Blob("layouts.zip", mimeTypeApplicationZip, baos.toByteArray());
195        } catch (final IOException ex) {
196            throw new FatalException("Unable to create zip of layouts", ex);
197        }
198    }
199
200    private static String zipEntryNameFor(ObjectSpecification objectSpec) {
201        final String fqn = objectSpec.getFullIdentifier();
202        return fqn.replace(".", File.separator)+".layout.json";
203    }
204
205
206    // //////////////////////////////////////
207
208    private SpecificationLoaderSpi specificationLoader;
209    private AdapterManager adapterManager;
210
211    @Programmatic
212    @Override
213    public void setSpecificationLoaderSpi(SpecificationLoaderSpi specificationLoader) {
214        this.specificationLoader = specificationLoader;
215    }
216
217    @Override
218    public void setAdapterManager(AdapterManager adapterManager) {
219        this.adapterManager = adapterManager;
220    }
221
222
223}