| /******************************************************************************* |
| * Copyright (c) 2008, 2014 Gunnar Wagenknecht and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Gunnar Wagenknecht - initial API and implementation |
| * Leo Ufimtsev lufimtse@redhat.com - fixed xml header issues. |
| * https://bugs.eclipse.org/381147 |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=276257 //used. |
| * |
| * |
| *******************************************************************************/ |
| package org.eclipse.releng.tools; |
| |
| import java.io.IOException; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| |
| |
| /* |
| * Test notes: |
| * [x] Empty Document |
| * [x] Empty Document with XML header |
| * [x] Document with content, no XML header. |
| * [x] Document with XML header and content on 2nd line |
| * [x] Document with XML header, copyright on first line with content on first line. |
| * [x] Document with XML header, content on the first line that doesn't close properly. |
| * Example: |
| * <?xml version="1.0" encoding="UTF-8"?><fragment><extension |
| * //Copy-right comment is executed correctly, '<fragment ..' is put onto new line. |
| * |
| * [x] Document with XML header, copyright on 2nd line, stuff. |
| * [x] test with non-IBM header. |
| * 2014.07.15 tested. |
| * |
| */ |
| |
| /** |
| * <h2> XML File handler. </h2> |
| * |
| * <p> This class deals with the special case of 'xml' files.</p> |
| * |
| * <p> * If an xml header exists for example: {@code <?xml version="1.0" encoding="UTF-8"?> } <br> |
| * then the copyright comment is inserted exactly at the end of it, on a new line, <br> |
| * moving any content down below the xml header. </p> |
| * |
| * <p> It does take into account multiple headers also. e.g:<br> |
| * {@code <?xml version="1.0" encoding="UTF-8"?>}<br> |
| {@code <?eclipse version="3.2"?> }<br> |
| In this case, the copy right comment is inserted below the last header. </p> |
| |
| * <p> If no xml header exists, then the copyright comment is inserted at the top of the xml file.<p> |
| */ |
| public class XmlFile extends SourceFile { |
| |
| public XmlFile(IFile file) { |
| super(file); |
| } |
| |
| /** |
| * Deals with the fact that XML files can start with a header and the copy <br> |
| * right comment can start at the end of the header. <br> |
| * |
| * <p> For example: {@code <?xml?> <!-- } </p> |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isCommentStart(String aLine) { |
| return aLine.trim().contains(getCommentStart()); |
| |
| //Note, above is a bit different from parent, contains/startswithd: |
| //Parent: |
| //return aLine.trim().STARTSWITH(getCommentStart()); |
| |
| } |
| |
| /** |
| * Deals with the fact that XML files can end with a header and the copy <br> |
| * right comment can start at the end of the header. <br> |
| * |
| * <p> For exaple: {@code <?xml?> <!-- } </p> |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isCommentEnd(String aLine, String commentStartString) { |
| return aLine.trim().contains(getCommentEnd()); |
| //Similarly, uses 'contains' instead of 'starts with' |
| } |
| |
| @Override |
| public String getCommentStart() { |
| return "<!--"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getCommentEnd() { |
| return "-->"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public int getFileType() { |
| return CopyrightComment.XML_COMMENT; |
| } |
| |
| /** |
| * Given the new constructed copyright comment, it inserts it into the the document. |
| * |
| * <p> Note, this is only called if inserting an actual comment.<br> |
| * If only updating the year, this method is not called. </p> |
| * @see org.eclipse.releng.tools.SourceFile#doInsert(java.lang.String, org.eclipse.jface.text.IDocument) |
| */ |
| @Override |
| protected void doInsert(final String comment, IDocument document) throws BadLocationException, IOException { |
| |
| //----------------- XML COMMENT CLEAN UP |
| // XML comments need extra-tidy up because we need to consider the existance of an XML header |
| String tidyComment = comment.trim(); |
| |
| //Append new-line at the end for cleaner look. |
| tidyComment += "\n"; |
| |
| // check for existance of an xml header (<?xml) |
| // If so, put the comment 'below' it. |
| // example: |
| //<?xml .... ?> |
| //<-- |
| // comment start.... |
| if (containsXmlEncoding(document)) { |
| // If encoding is present, pre-append a new line. |
| tidyComment = "\n" + tidyComment; //$NON-NLS-1$ |
| } |
| |
| //------------------ COMMENT INSERT |
| // find insert offset (we must skip instructions) |
| int insertOffset = findInsertOffset(document); |
| |
| // insert comment |
| document.replace(insertOffset, 0, tidyComment); |
| } |
| |
| /** |
| * Given the document, find the place after the xml header to insert the comment. |
| * @param document |
| * @return |
| * @throws BadLocationException |
| */ |
| private int findInsertOffset(IDocument document) throws BadLocationException { |
| boolean inInstruction = false; |
| int insertOffset = 0; |
| |
| for (int offset = 0; offset < document.getLength(); offset++) { |
| char c = document.getChar(offset); |
| |
| // ignore whitespace and new lines |
| if(Character.isWhitespace(c)) { |
| // we update the offset to ignore whitespaces |
| // after instruction ends |
| insertOffset = offset; |
| continue; |
| } |
| |
| // look at next char |
| char c2 = ((offset+1) < document.getLength()) ? document.getChar(offset+1) : 0; |
| |
| // look for instruction ending |
| if(inInstruction) { |
| if(c == '?' && c2 == '>') { |
| |
| //Offset is '+2' not '+1' because of '?' in '?>' |
| insertOffset = offset + 2; |
| inInstruction = false; |
| offset++; // don't need to analyse c2 again |
| // we continue in case there are more instructions |
| continue; |
| } else { |
| // look for ending |
| continue; |
| } |
| } |
| |
| // next chars must start an instruction |
| if(c == '<' && c2 =='?') { |
| inInstruction = true; |
| offset++; // don't need to analyse c2 again |
| continue; |
| } else { |
| // if it's something else, we can stop seeking |
| break; |
| } |
| } |
| return insertOffset; |
| } |
| |
| /** |
| * Find out if an XML document contains an XML meta header. |
| * |
| * <p> XML documents <b> sometimes </b> contain a header specifying various attributes such as <br> |
| * version, encoding etc... </p> |
| * |
| * <p> Examples include: <br> |
| * {@literal <?xml version="1.0" encoding="UTF-8"?> }<br> |
| * {@literal<?xml version="1.0" encoding="UTF-8" standalone="no"?> } <br> |
| * {@literal <?xml version="1.0" ?> } </p> |
| * |
| * @param xmlDoc |
| * @return True if it contains a header. |
| * @throws BadLocationException |
| */ |
| public boolean containsXmlEncoding(IDocument xmlDoc) throws BadLocationException { |
| |
| //XML attribute headers *must* reside on the first line. |
| //We identify if the xml document contains a header by checking the first tag and see if it starts with: <?xml |
| |
| //-- Check to see if the document is long enough to contain the minimum '<?xml?>' tag |
| if (xmlDoc.getLength() < 7) { |
| return false; |
| } |
| |
| for (int offset = 0; offset < xmlDoc.getLength(); offset++) { |
| |
| //Read Char. |
| char c = xmlDoc.getChar(offset); |
| |
| // ignore whitespace and new lines |
| if(Character.isWhitespace(c)) { |
| continue; |
| } |
| |
| //Once we've found the first '<', check that it's a '<?xml' |
| if (c == '<') { |
| |
| //check that document is long enough to close a header if we are to read it: '<?xml |
| if ((offset + 4) < xmlDoc.getLength()) { |
| |
| //Read "<?xml" equivalent. |
| String xmlTag = "" + c + xmlDoc.getChar(offset+1) + xmlDoc.getChar(offset+2) + //$NON-NLS-1$ |
| xmlDoc.getChar(offset+3) + xmlDoc.getChar(offset+4); |
| |
| if ( xmlTag.compareToIgnoreCase("<?xml") == 0) { //$NON-NLS-1$ |
| return true; |
| } else { |
| return false; |
| } |
| } |
| } |
| } |
| |
| //if parsing an empty xml document, return false. |
| return false; |
| } |
| |
| } |