blob: 3cd50a407d073c91e5869abf3718595fa57a6abd [file] [log] [blame]
/*
* 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.netbeans.modules.web.clientproject.sites;
import org.netbeans.modules.web.clientproject.api.sites.SiteHelper;
import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import javax.swing.JComponent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.modules.web.clientproject.ClientSideProjectConstants;
import org.netbeans.modules.web.clientproject.spi.SiteTemplateImplementation;
import org.netbeans.modules.web.clientproject.util.FileUtilities;
import org.netbeans.modules.web.clientproject.util.FileUtilities.ZipEntryFilter;
import org.netbeans.modules.web.clientproject.util.FileUtilities.ZipEntryTask;
import org.openide.filesystems.FileObject;
import org.openide.util.ChangeSupport;
import org.openide.util.EditableProperties;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
/**
* Special {@link SiteTemplateImplementation} (not registered in SFS).
*/
public class SiteZip implements SiteTemplateImplementation {
static final Logger LOGGER = Logger.getLogger(SiteZip.class.getName());
private static final String USED_TEMPLATES = "last.templates"; //NOI18N
private static final String SEPARATOR = "=s e p="; //NOI18N
private static final ZipEntryFilter NB_TEMPLATE_FILTER = new ZipEntryFilter() {
@Override
public boolean accept(ZipEntry zipEntry) {
return !zipEntry.isDirectory()
&& zipEntry.getName().equals(ClientSideProjectConstants.TEMPLATE_DESCRIPTOR);
}
};
private Customizer cust;
@Override
public String getId() {
return "ARCHIVE"; // NOI18N
}
@NbBundle.Messages("SiteZip.name=Archive File")
@Override
public String getName() {
return Bundle.SiteZip_name();
}
@Override
public String getDescription() {
return getName();
}
public Customizer getCustomizer() {
cust = new Customizer();
return cust;
}
@Override
public boolean isPrepared() {
return getArchiveFile().isFile();
}
@Override
public void prepare() throws IOException {
assert !EventQueue.isDispatchThread();
assert !isPrepared();
String template = cust.panel.getTemplate();
assert isRemoteUrl(template) : "Remote URL expected: " + template; //NOI18N
SiteHelper.download(template, getArchiveFile(), null); // NOI18N
}
@Override
public void configure(final ProjectProperties projectProperties) {
assert !EventQueue.isDispatchThread();
assert isPrepared();
try {
FileUtilities.runOnZipEntries(getArchiveFile(), new ZipEntryTask() {
@Override
public void run(ZipEntry zipEntry) {
// noop
}
@Override
public void run(InputStream zipEntryInputStream) {
EditableProperties templateProperties = new EditableProperties(false);
try {
templateProperties.load(zipEntryInputStream);
projectProperties.setSiteRootFolder(templateProperties.getProperty(ClientSideProjectConstants.PROJECT_SITE_ROOT_FOLDER))
.setSourceFolder(templateProperties.getProperty(ClientSideProjectConstants.PROJECT_SOURCE_FOLDER))
.setTestFolder(templateProperties.getProperty(ClientSideProjectConstants.PROJECT_TEST_FOLDER));
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "Error while reading file", ex);
}
}
}, NB_TEMPLATE_FILTER);
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "Error while reading zip file", ex);
}
}
@Override
public void apply(FileObject projectDir, ProjectProperties projectProperties, ProgressHandle handle) throws IOException {
assert !EventQueue.isDispatchThread();
if (!isPrepared()) {
// not correctly prepared, user has to know about it already
LOGGER.info("Template not correctly prepared, nothing to be applied"); //NOI18N
return;
}
SiteHelper.unzipProjectTemplate(getTargetDir(projectDir, projectProperties), getArchiveFile(), handle, ClientSideProjectConstants.TEMPLATE_DESCRIPTOR);
registerTemplate(cust.panel.getTemplate());
}
/**
* Return project dir for NB template, site root otherwise.
*/
private FileObject getTargetDir(FileObject projectDir, ProjectProperties projectProperties) throws IOException {
if (FileUtilities.listZipFiles(getArchiveFile(), NB_TEMPLATE_FILTER).isEmpty()) {
// not nb template
String siteRootFolder = projectProperties.getSiteRootFolder();
assert siteRootFolder != null;
return projectDir.getFileObject(siteRootFolder);
}
return projectDir;
}
@Override
public void cleanup() {
String template = cust.panel.getTemplate();
if (isRemoteUrl(template)) {
// noop
return;
}
File archiveFile = getArchiveFile();
if (archiveFile.isFile()) {
if (!archiveFile.delete()) {
archiveFile.deleteOnExit();
}
}
}
private File getArchiveFile() {
String template = cust.panel.getTemplate();
if (!isRemoteUrl(template)) {
return new File(template);
}
// remote file => calculate hash of its url
CRC32 crc = new CRC32();
crc.update(template.getBytes(StandardCharsets.UTF_8));
String filename = String.valueOf(crc.getValue()) + ".zip"; // NOI18N
LOGGER.log(Level.INFO, "Remote URL \"{0}\" set, downloaded to {1}", new Object[] {template, filename}); //NOI18N
return new File(SiteHelper.getJsLibsDirectory(), filename);
}
private boolean isRemoteUrl(String input) {
return input.toLowerCase().startsWith("http"); // NOI18N
}
public static void registerTemplate(File f) {
String name = f.getAbsolutePath();
registerTemplate(name);
}
public static void registerTemplate(String name) {
String templates = NbPreferences.forModule(SiteZip.class).get(USED_TEMPLATES, ""); //NOI18N
templates = name + SEPARATOR + templates.replace(name+SEPARATOR, ""); //NOI18N
NbPreferences.forModule(SiteZip.class).put(USED_TEMPLATES, templates);
}
public static List<String> getUsedTemplates() {
String templates = NbPreferences.forModule(SiteZip.class).get(USED_TEMPLATES, ""); //NOI18N
return Arrays.asList(templates.split(SEPARATOR));
}
//~ Inner classes
public static class Customizer {
private SiteZipPanel panel = new SiteZipPanel(this);
private ChangeSupport sup = new ChangeSupport(this);
private String error = ""; //NOI18N
public void addChangeListener(ChangeListener listener) {
sup.addChangeListener(listener);
}
public void removeChangeListener(ChangeListener listener) {
sup.removeChangeListener(listener);
}
public JComponent getComponent() {
return panel;
}
@NbBundle.Messages({
"SiteZip.error.template.missing=Template file must be specified.",
"SiteZip.error.template.invalid=Template file is invalid (http://... or local file expected).",
"SiteZip.error.template.notZip=Template file must be ZIP archive (*.zip)."
})
public boolean isValid() {
String tpl = panel.getTemplate();
if (tpl.isEmpty()) {
error = Bundle.SiteZip_error_template_missing();
return false;
}
File localTpl = new File(tpl);
if (!tpl.startsWith("http") && !localTpl.isFile()) { //NOI18N
error = Bundle.SiteZip_error_template_invalid();
return false;
}
if (localTpl.isFile()
&& !localTpl.getName().endsWith(".zip")) { // NOI18N
error = Bundle.SiteZip_error_template_notZip();
return false;
}
error = null;
return true;
}
public String getErrorMessage() {
return error;
}
public String getWarningMessage() {
return null;
}
void fireChange() {
sup.fireChange();
}
}
}