Commit 7ef9774a authored by Romain Bioteau's avatar Romain Bioteau Committed by GitHub
Browse files

feat(app) add warning for deep menus (#401)

Closes BS-16480
parent 69456e07
......@@ -16,11 +16,14 @@ Require-Bundle: org.bonitasoft.studio.common.repository;bundle-version="7.5.0",
org.eclipse.wst.xml.core;bundle-version="1.1.902",
org.eclipse.ui.ide,
org.eclipse.jface.databinding,
org.bonitasoft.studio.tests-utils;bundle-version="6.4.1";resolution:=optional,
org.eclipse.wst.validation,
org.eclipse.ui.editors;bundle-version="3.8.200",
org.eclipse.wst.xml.ui;bundle-version="1.1.501",
org.bonitasoft.studio.tests-utils;bundle-version="6.4.1";resolution:=optional,
org.junit;bundle-version="4.11.0";resolution:=optional,
assertj-core;bundle-version="3.6.1";resolution:=optional,
org.mockito;bundle-version="1.10.19";resolution:=optional
org.mockito;bundle-version="1.10.19";resolution:=optional,
org.eclipse.jface.text
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.bonitasoft.engine.business.application,
org.bonitasoft.engine.business.application.exporter,
......
......@@ -53,3 +53,4 @@ exportApplicationDescriptorMessage=You are about to export an application descri
unparsableApplicationFile=Unable to parse xml
rename=Rename
renameFile=Rename file
menuLevelWarning=Level 2+ menus are not supported by the default layout page
......@@ -67,5 +67,35 @@
name="%ApplicationPerspective">
</perspective>
</extension>
<extension
id="id1"
name="name"
point="org.eclipse.wst.validation.validatorV2">
<validator
class="org.bonitasoft.studio.la.ui.validator.MenuLevelValidator">
<include>
<rules>
<contentType
exactMatch="true"
id="org.bonitasoft.studio.la.applicationDescriptor">
</contentType>
</rules>
</include>
</validator>
</extension>
<extension
point="org.eclipse.core.contenttype.contentTypes">
<content-type
base-type="org.eclipse.core.runtime.xml"
default-charset="UTF-8"
file-extensions="xml"
id="org.bonitasoft.studio.la.applicationDescriptor"
name="Application Descriptor"
priority="normal">
<describer
class="org.bonitasoft.studio.la.core.ApplicationContentDescriber">
</describer>
</content-type>
</extension>
</plugin>
/**
* Copyright (C) 2017 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.la.ui.validator;
import static org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder.newApplication;
import static org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder.newApplicationContainer;
import static org.bonitasoft.studio.fakes.IResourceFakesBuilder.anIFile;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder;
import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;
import org.eclipse.core.resources.IFile;
import org.junit.Test;
public class MenuLevelValidatorTest {
@Test
public void should_add_warning_message() throws Exception {
final IFile resource = anIFile().build();
final MenuLevelValidator menuLevelValidator = spy(new MenuLevelValidator());
doReturn(appWithDeepMenus()).when(menuLevelValidator)
.toApplicationContainer(resource);
doReturn(null).when(menuLevelValidator).createMessage(resource);
menuLevelValidator.validate(resource, 0, null, null);
verify(menuLevelValidator, times(1)).createMessage(resource);
}
@Test
public void should_not_add_warning_message() throws Exception {
final IFile resource = anIFile().build();
final MenuLevelValidator menuLevelValidator = spy(new MenuLevelValidator());
doReturn(appWithSingleMenus()).when(menuLevelValidator)
.toApplicationContainer(resource);
doReturn(null).when(menuLevelValidator).createMessage(resource);
menuLevelValidator.validate(resource, 0, null, null);
verify(menuLevelValidator, never()).createMessage(resource);
}
private ApplicationNodeContainer appWithDeepMenus() {
return newApplicationContainer()
.havingApplications(
newApplication("", "", "").havingApplicationMenus(ApplicationNodeBuilder.newMenu("level0", "")
.havingMenu(ApplicationNodeBuilder.newMenu("level1", "")
.havingMenu(ApplicationNodeBuilder.newMenu("level2", "")))))
.create();
}
private ApplicationNodeContainer appWithSingleMenus() {
return newApplicationContainer()
.havingApplications(
newApplication("", "", "").havingApplicationMenus(ApplicationNodeBuilder.newMenu("level0", "")
.havingMenu(ApplicationNodeBuilder.newMenu("level1", ""))))
.create();
}
}
/*******************************************************************************
* Copyright (C) 2017 BonitaSoft S.A.
* BonitaSoft is a trademark of BonitaSoft SA.
* This software file is BONITASOFT CONFIDENTIAL. Not For Distribution.
* For commercial licensing information, contact:
* BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
* or BonitaSoft US, 51 Federal Street, Suite 305, San Francisco, CA 94107
*******************************************************************************/
package org.bonitasoft.studio.la.core;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.ITextContentDescriber;
import org.eclipse.core.runtime.content.XMLContentDescriber;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
public class ApplicationContentDescriber extends XMLContentDescriber implements ITextContentDescriber {
private static final String APPLICATION_NS = "http://documentation.bonitasoft.com/application-xml-schema/1.0";
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.content.XMLContentDescriber#describe(java.io.Reader, org.eclipse.core.runtime.content.IContentDescription)
*/
@Override
public int describe(Reader input, IContentDescription description) throws IOException {
if (super.describe(input, description) == VALID) {
return validApplicationXML(CharStreams.toString(input));
}
return INVALID;
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.content.XMLContentDescriber#describe(java.io.InputStream, org.eclipse.core.runtime.content.IContentDescription)
*/
@Override
public int describe(InputStream input, IContentDescription description) throws IOException {
if (super.describe(input, description) == VALID) {
return validApplicationXML(new String(ByteStreams.toByteArray(input)));
}
return INVALID;
}
private int validApplicationXML(final String stringContent) throws IOException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
try (InputStream is = new ByteArrayInputStream(stringContent.getBytes())) {
final Document document = dbf.newDocumentBuilder().parse(is);
final Element documentElement = document.getDocumentElement();
return APPLICATION_NS.equals(documentElement.getNamespaceURI()) ? VALID : INVALID;
} catch (SAXException | ParserConfigurationException e) {
return stringContent.contains(APPLICATION_NS) ? VALID : INVALID;
}
}
}
......@@ -73,6 +73,7 @@ public class Messages extends NLS {
public static String unparsableApplicationFile;
public static String rename;
public static String renameFile;
public static String menuLevelWarning;
static {
NLS.initializeMessages("messages", Messages.class);
......
/**
* Copyright (C) 2017 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.la.ui.validator;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;
import javax.xml.bind.JAXBException;
import org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter;
import org.bonitasoft.engine.business.application.xml.ApplicationMenuNode;
import org.bonitasoft.engine.business.application.xml.ApplicationNode;
import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;
import org.bonitasoft.studio.la.i18n.Messages;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.wst.validation.AbstractValidator;
import org.eclipse.wst.validation.ValidationResult;
import org.eclipse.wst.validation.ValidationState;
import org.eclipse.wst.validation.ValidatorMessage;
import org.xml.sax.SAXException;
import com.google.common.io.ByteStreams;
public class MenuLevelValidator extends AbstractValidator {
private final ApplicationNodeContainerConverter converter = new ApplicationNodeContainerConverter();
/*
* (non-Javadoc)
* @see org.eclipse.wst.validation.AbstractValidator#validate(org.eclipse.core.resources.IResource, int, org.eclipse.wst.validation.ValidationState,
* org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public ValidationResult validate(IResource resource, int kind, ValidationState state, IProgressMonitor monitor) {
if (resource.getType() != IResource.FILE) {
return null;
}
final ValidationResult validationResult = new ValidationResult();
final Optional<ApplicationNodeContainer> appContainer = Optional
.ofNullable(toApplicationContainer((IFile) resource));
appContainer.map(ApplicationNodeContainer::getApplications)
.map(Collection::stream).orElse(Stream.empty())
.map(ApplicationNode::getApplicationMenus)
.flatMap(Collection::stream)
.map(ApplicationMenuNode::getApplicationMenus)
.flatMap(Collection::stream)
.filter(menuNode -> !menuNode.getApplicationMenus().isEmpty())
.map(ApplicationMenuNode::getApplicationMenus)
.flatMap(Collection::stream)
.map(menuNode -> createMessage(resource))
.forEach(validationResult::add);
return validationResult;
}
protected ValidatorMessage createMessage(IResource resource) {
final ValidatorMessage warningMessage = ValidatorMessage
.create(Messages.menuLevelWarning, resource);
warningMessage.setAttribute(IMarker.LOCATION, resource.getName());
warningMessage.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
warningMessage.setAttribute(IMarker.CHAR_START, 0);
warningMessage.setAttribute(IMarker.CHAR_END, 0);
return warningMessage;
}
protected ApplicationNodeContainer toApplicationContainer(IFile resource) {
try (InputStream is = resource.getContents()) {
return converter.unmarshallFromXML(ByteStreams.toByteArray(is));
} catch (IOException | CoreException | JAXBException | SAXException e) {
return null;
}
}
}
......@@ -16,6 +16,7 @@ package org.bonitasoft.studio.fakes;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStream;
......@@ -27,11 +28,15 @@ import org.eclipse.core.runtime.CoreException;
public class IResourceFakesBuilder<T extends IResource> {
public static IResourceFakesBuilder<IFile> anIFile() {
return new IResourceFakesBuilder<IFile>(mock(IFile.class));
final IFile mock = mock(IFile.class);
when(mock.getType()).thenReturn(IResource.FILE);
return new IResourceFakesBuilder<>(mock);
}
public static IResourceFakesBuilder<IFolder> anIFolder() {
return new IResourceFakesBuilder<IFolder>(mock(IFolder.class));
final IFolder mock = mock(IFolder.class);
when(mock.getType()).thenReturn(IResource.FOLDER);
return new IResourceFakesBuilder<>(mock);
}
private final IResource resourceFake;
......@@ -60,7 +65,7 @@ public class IResourceFakesBuilder<T extends IResource> {
}
try {
doReturn(resourceAsStream).when((IFile) resourceFake).getContents();
} catch (CoreException e) {
} catch (final CoreException e) {
return (IResourceFakesBuilder<IFile>) this;
}
return (IResourceFakesBuilder<IFile>) this;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment