/*******************************************************************************
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*
*******************************************************************************/
package com.liferay.ide.xml.search.ui.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import com.liferay.ide.core.util.ReflectionUtil;
import com.liferay.ide.ui.tests.UITestsUtils;
import com.liferay.ide.xml.search.ui.editor.CompoundRegion;
import com.liferay.ide.xml.search.ui.editor.InfoRegion;
import com.liferay.ide.xml.search.ui.editor.MarkerRegion;
import com.liferay.ide.xml.search.ui.editor.TemporaryRegion;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.wst.sse.ui.internal.ExtendedConfigurationBuilder;
import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
import org.eclipse.wst.validation.internal.ValManager;
import org.eclipse.wst.validation.internal.ValOperation;
import org.eclipse.wst.validation.internal.ValType;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Some methods are modified from eclipse wst sse tests
*
* @author Kuo Zhang
* @author Terry Jia
*/
@SuppressWarnings( "restriction" )
public class XmlSearchTestsUtils extends UITestsUtils
{
private static ValManager valManager = ValManager.getDefault();
public static void buildAndValidate( IFile file ) throws Exception
{
valManager.validate( file.getProject(), file, IResourceDelta.CHANGED, ValType.Build,
IncrementalProjectBuilder.FULL_BUILD, new ValOperation(), new NullProgressMonitor() );
}
public static boolean checkMarkerByMessage( IResource resource, String markerType,
String markerMessage, boolean fullMatch ) throws Exception
{
return findMarkerByMessage( resource, markerType, markerMessage, fullMatch ) != null;
}
public static boolean checkNoMarker( IResource resource, String markerType ) throws Exception
{
return resource.findMarkers( markerType, false, IResource.DEPTH_ZERO ).length == 0;
}
// check if the expected hyperlink is in the given proposals
public static boolean containHyperlink( IHyperlink[] hyperlinks, String expectedHyperlinkText, boolean fullMatch )
{
for( IHyperlink hyperlink : hyperlinks )
{
if( fullMatch && hyperlink.getHyperlinkText().equals( expectedHyperlinkText ) )
{
return true;
}
else if( ! fullMatch && hyperlink.getHyperlinkText().contains( expectedHyperlinkText ) )
{
return true;
}
}
return false;
}
public static IMarker findMarkerByMessage( IResource resource, String markerType,
String markerMessage, boolean fullMatch ) throws Exception
{
resource.refreshLocal( IResource.DEPTH_ZERO, new NullProgressMonitor() );
final IMarker[] markers = resource.findMarkers( markerType, false, IResource.DEPTH_ZERO );
for( IMarker marker : markers )
{
if( fullMatch && marker.getAttribute( IMarker.MESSAGE ).toString().equals( markerMessage ) )
{
return marker;
}
else if( ! fullMatch && marker.getAttribute( IMarker.MESSAGE ).toString().matches( markerMessage ) )
{
return marker;
}
}
return null;
}
public static IMarkerResolution findMarkerResolutionByClass( IMarker marker, Class<? extends IMarkerResolution> clazz )
{
IMarkerResolution[] markerResolutions = IDE.getMarkerHelpRegistry().getResolutions( marker );
for( IMarkerResolution markerResolution : markerResolutions )
{
if( markerResolution.getClass().equals( clazz ) )
{
return markerResolution;
}
}
return null;
}
public static int getAttrValueOffset( IFile file, String elementName, String attrName ) throws Exception
{
final IDOMModel domModel = getDOMModel( file, false );
final Node attrNode = domModel.getDocument().getElementsByTagName( elementName ).
item( 0 ).getAttributes().getNamedItem( attrName );
int retval = getRegion( attrNode ).getOffset();
domModel.releaseFromRead();
return retval;
}
public static int getElementContentOffset( IFile file, String elementName ) throws Exception
{
final IDOMModel model = getDOMModel( file, false );
final Node element = model.getDocument().getElementsByTagName( elementName ).item( 0 );
int retval = getRegion( element.getFirstChild() ).getOffset();
model.releaseFromRead();
return retval;
}
private static IHyperlink[] getHyperLinks( IFile file, int nodeType, String... nodeNames ) throws Exception
{
List<IHyperlink> retval = new ArrayList<IHyperlink>();
IDOMModel domModel = null;
Node targetNode = null;
final StructuredTextViewer viewer = getEditor( file ).getTextViewer();
final SourceViewerConfiguration conf = getSourceViewerConfiguraionFromExtensionPoint( file );
if( nodeType == Node.ELEMENT_NODE )
{
String elementName = nodeNames[0];
domModel = getDOMModel( file, false );
// the actual node is text node of this.element
targetNode = domModel.getDocument().getElementsByTagName( elementName ).item( 0 ).getFirstChild();
}
else if( nodeType == Node.ATTRIBUTE_NODE )
{
String elementName = nodeNames[0];
String attrName = nodeNames[1];
domModel = getDOMModel( file, false );
NodeList elements = domModel.getDocument().getElementsByTagName( elementName );
targetNode = getElementByAttr( elements, attrName ).getAttributes().getNamedItem( attrName );
elements = domModel.getDocument().getElementsByTagName( elementName );
}
else
{
return new IHyperlink[0];
}
assertNotNull( targetNode );
viewer.refresh();
final IHyperlinkDetector[] hyperlinkDetectors = conf.getHyperlinkDetectors( viewer );
final IRegion region = getRegion( targetNode );
for( IHyperlinkDetector detector : hyperlinkDetectors )
{
IHyperlink[] tempHyperlinks = detector.detectHyperlinks( viewer, region, true );
if( tempHyperlinks != null && tempHyperlinks.length > 0 )
{
retval.addAll( Arrays.asList( tempHyperlinks ) );
}
}
domModel.releaseFromRead();
return retval.toArray( new IHyperlink[0] );
}
public static IHyperlink[] getHyperLinksForAttr( IFile file, String elementName, String attrName ) throws Exception
{
return getHyperLinks( file, Node.ATTRIBUTE_NODE, elementName, attrName );
}
public static IHyperlink[] getHyperLinksForElement( IFile file, String elementName ) throws Exception
{
return getHyperLinks( file, Node.ELEMENT_NODE, elementName );
}
private static ICompletionProposal[] getProposals( IFile file, int nodeType, String... nodeNames ) throws Exception
{
Node targetNode = null;
IDOMModel domModel = null;
final StructuredTextViewer viewer = getEditor( file ).getTextViewer();
final SourceViewerConfiguration srcViewConf = getSourceViewerConfiguraionFromExtensionPoint( file );
viewer.refresh();
if( nodeType == Node.ELEMENT_NODE )
{
String elementName = nodeNames[0];
domModel = getDOMModel( file, false );
targetNode = domModel.getDocument().getElementsByTagName( elementName ).item( 0 );
}
else if( nodeType == Node.ATTRIBUTE_NODE )
{
String elementName = nodeNames[0];
String attrName = nodeNames[1];
domModel = getDOMModel( file, false );
NodeList elements = domModel.getDocument().getElementsByTagName( elementName );
Element element = getElementByAttr( elements, attrName );
targetNode = element.getAttributes().getNamedItem( attrName );
}
if( nodeType == Node.TEXT_NODE )
{
String elementName = nodeNames[0];
domModel = getDOMModel( file, false );
targetNode = domModel.getDocument().getElementsByTagName( elementName ).item( 0 ).getFirstChild();
}
int offset = getRegion( targetNode ).getOffset() + getRegion( targetNode ).getLength();
final ContentAssistant contentAssistant = (ContentAssistant) srcViewConf.getContentAssistant( viewer );
// viewer.configure( srcViewConf );
// viewer.setSelectedRange( offset, 0 );
// get the processor
final String partitionTypeID = viewer.getDocument().getPartition( offset ).getType();
final IContentAssistProcessor processor = contentAssistant.getContentAssistProcessor( partitionTypeID );
// get content assist suggestions
final ICompletionProposal[] proposals = processor.computeCompletionProposals( viewer, offset );
domModel.releaseFromRead();
return proposals;
}
public static ICompletionProposal[] getProposalsForAttr( IFile file, String elementName ,String attrName ) throws Exception
{
return getProposals( file, Node.ATTRIBUTE_NODE, elementName, attrName );
}
public static ICompletionProposal[] getProposalsForElement( IFile file, String elementName ) throws Exception
{
return getProposals( file, Node.ELEMENT_NODE, elementName );
}
public static ICompletionProposal[] getProposalsForText( IFile file, String... nodeNames) throws Exception
{
return getProposals( file, Node.TEXT_NODE, nodeNames);
}
// get the SourceViewerConfiguration from extension point
public static SourceViewerConfiguration getSourceViewerConfiguraionFromExtensionPoint( IFile file ) throws Exception
{
final String contentTypeId = file.getContentDescription().getContentType().getId();
// get Source Viewer Configuration from content type
Object viewerConfFromContentType =
ExtendedConfigurationBuilder.getInstance().getConfiguration(
ExtendedConfigurationBuilder.SOURCEVIEWERCONFIGURATION, contentTypeId );
// get Source Viewer Configuration from editorId, has a higher priority
final String editorId = IDE.getEditorDescriptor( file ).getId();
Object viewerConfFromEditor =
ExtendedConfigurationBuilder.getInstance().getConfiguration(
ExtendedConfigurationBuilder.SOURCEVIEWERCONFIGURATION, editorId );
if( viewerConfFromEditor != null && viewerConfFromEditor instanceof SourceViewerConfiguration )
{
return (SourceViewerConfiguration) viewerConfFromEditor;
}
else if( viewerConfFromContentType != null && viewerConfFromContentType instanceof SourceViewerConfiguration )
{
return (SourceViewerConfiguration) viewerConfFromContentType;
}
return null;
}
private static String[] getTextHover( IFile file, int nodeType, String... nodeNames ) throws Exception
{
List<String> retval = new ArrayList<String>();
IDOMModel domModel = null;
Node targetNode = null;
final StructuredTextViewer viewer = getEditor( file ).getTextViewer();
if( nodeType == Node.ELEMENT_NODE )
{
String elementName = nodeNames[0];
domModel = getDOMModel( file, false );
// the actual node is text node of this.element
targetNode = domModel.getDocument().getElementsByTagName( elementName ).item( 0 ).getFirstChild();
}
else if( nodeType == Node.ATTRIBUTE_NODE )
{
String elementName = nodeNames[0];
String attrName = nodeNames[1];
domModel = getDOMModel( file, false );
NodeList elements = domModel.getDocument().getElementsByTagName( elementName );
Element element = getElementByAttr( elements, attrName );
targetNode = element.getAttributes().getNamedItem( attrName );
}
else
{
return null;
}
int offset = getRegion( targetNode ).getOffset();
final Method getTextHoverMethod =
ReflectionUtil.getDeclaredMethod( viewer.getClass(), "getTextHover", true, int.class, int.class );
getTextHoverMethod.setAccessible( true );
final ITextHover hover = (ITextHover)getTextHoverMethod.invoke( viewer, offset ,0 );
final IRegion region = hover.getHoverRegion( viewer, offset );
if( region instanceof CompoundRegion )
{
List<IRegion> regions = ( (CompoundRegion) region ).getRegions();
for( IRegion reg : regions )
{
if( reg instanceof TemporaryRegion )
{
String info = ( (TemporaryRegion) reg ).getAnnotation().getText();
retval.add( info );
}
if( reg instanceof MarkerRegion )
{
MarkerAnnotation annotation = ( (MarkerRegion) reg ).getAnnotation();
String info = ( annotation.getMarker().getAttribute( IMarker.MESSAGE ) ).toString();
retval.add( info );
}
if( reg instanceof InfoRegion )
{
retval.add( ( (InfoRegion) reg ).getInfo() );
}
}
}
domModel.releaseFromRead();
return retval.toArray( new String[0] );
}
public static String[] getTextHoverForAttr( IFile file, String elementName, String attrName ) throws Exception
{
return getTextHover( file, Node.ATTRIBUTE_NODE, elementName, attrName );
}
public static String[] getTextHoverForElement( IFile file, String elementName ) throws Exception
{
return getTextHover( file, Node.ELEMENT_NODE, elementName );
}
// open the editor during test, useless for test, but testers can see what's going on.
public static void openEditor( IFile file ) throws Exception
{
getEditor( file );
}
// set the attribute value for the 1st element with the "elementName"
public static void setAttrValue( IFile file, String elementName, String attrName, String attrValue ) throws Exception
{
final IDOMModel domModel = getDOMModel( file, true );
assertNotNull( domModel );
final IDOMDocument document = domModel.getDocument();
final NodeList elements = document.getElementsByTagName( elementName );
Element element = getElementByAttr( elements, attrName );
assertNotNull( element );
Attr attrNode = element.getAttributeNode( attrName );
attrNode.setValue( attrValue );
domModel.save();
domModel.releaseFromEdit();
file.refreshLocal( IResource.DEPTH_ZERO, new NullProgressMonitor() );
}
public static Element getElementByAttr( NodeList elements, String attrName ) throws Exception
{
Element element = null;
for( int i = 0; i < elements.getLength(); i++ )
{
element = (Element) elements.item( i );
if( element.hasAttribute( attrName ) )
break;
}
return element;
}
// set the content for the 1st element with name of "elementName"
public static void setElementContent( IFile file, String elementName, String content ) throws Exception
{
final IDOMModel domModel = getDOMModel( file, true );
assertNotNull( domModel );
final IDOMDocument document = domModel.getDocument();
final NodeList elements = document.getElementsByTagName( elementName );
assertEquals( true, elements.getLength() > 0 );
final Element element = (Element) elements.item( 0 );
final NodeList childNodes = element.getChildNodes();
for( int i = 0; i < childNodes.getLength(); i++ )
{
element.removeChild( childNodes.item( i ) );
}
element.appendChild( document.createTextNode( content ) );
domModel.save();
domModel.releaseFromEdit();
file.refreshLocal( IResource.DEPTH_ZERO, new NullProgressMonitor() );
}
// find the marker, use the given resolution to fix it and check if the marker is gone.
public static void verifyQuickFix( IFile file, String markerType, String markerMessageRegex,
Class<? extends IMarkerResolution> resolutionClazz ) throws Exception
{
IMarker expectedMarker = findMarkerByMessage( file, markerType, markerMessageRegex, false );
assertNotNull( expectedMarker );
IMarkerResolution expectedMarkerResolution =
findMarkerResolutionByClass( expectedMarker, resolutionClazz );
assertNotNull( expectedMarkerResolution );
expectedMarkerResolution.run( expectedMarker );
buildAndValidate( file );
expectedMarker = findMarkerByMessage( file, markerType, markerMessageRegex, false );
assertNull( expectedMarker );
}
}