UseExplicitEncodingFixCore.java

/*******************************************************************************
 * Copyright (c) 2021 Carsten Hammer.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Carsten Hammer
 *******************************************************************************/
package org.sandbox.jdt.internal.corext.fix;

import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.text.edits.TextEditGroup;
import org.sandbox.jdt.internal.common.ReferenceHolder;
import org.sandbox.jdt.internal.corext.fix.helper.AbstractExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.ChangeBehavior;
import org.sandbox.jdt.internal.corext.fix.helper.ByteArrayOutputStreamExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.ChannelsNewReaderExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.ChannelsNewWriterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.CharsetForNameExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FileReaderExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FileWriterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FilesNewBufferedReaderExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FilesNewBufferedWriterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FilesReadAllLinesExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FilesReadStringExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FilesWriteStringExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.FormatterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.InputStreamReaderExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.OutputStreamWriterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.PrintStreamExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.PrintWriterExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.PropertiesStoreToXMLExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.ScannerExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.StringExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.StringGetBytesExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.URLDecoderDecodeExplicitEncoding;
import org.sandbox.jdt.internal.corext.fix.helper.URLEncoderEncodeExplicitEncoding;
import org.sandbox.jdt.internal.ui.fix.MultiFixMessages;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;

public enum UseExplicitEncodingFixCore {

	CHARSET(new CharsetForNameExplicitEncoding(), true),
	CHANNELSNEWREADER(new ChannelsNewReaderExplicitEncoding(), true),
	CHANNELSNEWWRITER(new ChannelsNewWriterExplicitEncoding(), true),
	STRING_GETBYTES(new StringGetBytesExplicitEncoding(), true),
	STRING(new StringExplicitEncoding(), true),
	INPUTSTREAMREADER(new InputStreamReaderExplicitEncoding(), true),
	OUTPUTSTREAMWRITER(new OutputStreamWriterExplicitEncoding(), true),
	FILEREADER(new FileReaderExplicitEncoding(), false),
	FILEWRITER(new FileWriterExplicitEncoding(), false),
	PRINTWRITER(new PrintWriterExplicitEncoding(), false),
	PRINTSTREAM(new PrintStreamExplicitEncoding(), true),
	BYTEARRAYOUTPUTSTREAM(new ByteArrayOutputStreamExplicitEncoding(), false),
	FORMATTER(new FormatterExplicitEncoding(), true),
	URLDECODER(new URLDecoderDecodeExplicitEncoding(), true),
	URLENCODER(new URLEncoderEncodeExplicitEncoding(), true),
	SCANNER(new ScannerExplicitEncoding(), true),
	PROPERTIES_STORETOXML(new PropertiesStoreToXMLExplicitEncoding(), true),
	FILES_NEWBUFFEREDREADER(new FilesNewBufferedReaderExplicitEncoding(), false),
	FILES_NEWBUFFEREDWRITER(new FilesNewBufferedWriterExplicitEncoding(), false),
	FILES_READALLLINES(new FilesReadAllLinesExplicitEncoding(), false),
	FILES_READSTRING(new FilesReadStringExplicitEncoding(), false),
	FILES_WRITESTRING(new FilesWriteStringExplicitEncoding(), false);

	AbstractExplicitEncoding<ASTNode> explicitencoding;
	private final boolean dslHandled;

	@SuppressWarnings("unchecked")
	UseExplicitEncodingFixCore(AbstractExplicitEncoding<? extends ASTNode> explicitencoding, boolean dslHandled) {
		this.explicitencoding=(AbstractExplicitEncoding<ASTNode>) explicitencoding;
		this.dslHandled=dslHandled;
	}

	/**
	 * Returns whether this fix is fully handled by DSL rules in the encoding
	 * {@code .sandbox-hint} file.
	 *
	 * <p>When {@code true}, the pattern has corresponding DSL rules in
	 * {@code encoding.sandbox-hint} that cover string-based charset argument
	 * replacement and zero-argument/missing-encoding patterns.
	 * Tier 3 patterns (structural rewrites like FileReader→InputStreamReader,
	 * FileWriter→OutputStreamWriter, PrintWriter→BufferedWriter) remain
	 * imperative-only ({@code false}) because they require complex AST
	 * restructuring that the DSL cannot express.</p>
	 *
	 * <p>Some patterns are {@code false} despite having DSL rules because the
	 * DSL only handles a subset (e.g., zero-arg expansion) while the imperative
	 * helper also handles FQN-shortening of existing Charset arguments.
	 * For these, the imperative helper runs as a fallback, and the
	 * {@code nodesprocessed} set prevents double-processing.</p>
	 *
	 * @return {@code true} if this fix is fully covered by DSL rules
	 */
	public boolean isDslHandled() {
		return dslHandled;
	}

	public String getPreview(boolean i, ChangeBehavior cb) {
		long countother= explicitencoding.getPreview(!i, cb).lines().count();
		StringBuilder preview= new StringBuilder(explicitencoding.getPreview(i,cb));
		long countnow= preview.toString().lines().count();
		if(countnow<countother) {
			for (long ii=0;ii<countother-countnow;ii++) {
				preview.append(System.lineSeparator());
			}
		}
		return preview.toString()+System.lineSeparator();
	}
	/**
	 * Compute set of CompilationUnitRewriteOperation to refactor supported situations using default encoding to make use of explicit calls
	 *
	 * @param compilationUnit unit to search in
	 * @param operations set of all CompilationUnitRewriteOperations created already
	 * @param nodesprocessed list to remember nodes already processed
	 * @param cb distinguish if you want to keep the same behavior or get code independent of environment
	 */
	public void findOperations(final CompilationUnit compilationUnit,final Set<CompilationUnitRewriteOperation> operations,final Set<ASTNode> nodesprocessed, ChangeBehavior cb) {
		explicitencoding.find(this, compilationUnit, operations, nodesprocessed, cb);
	}

	public CompilationUnitRewriteOperation rewrite(final ASTNode visited, ChangeBehavior cb, ReferenceHolder<ASTNode, Object> data) {
		return new CompilationUnitRewriteOperation() {
			@Override
			public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
				TextEditGroup group= createTextEditGroup(Messages.format(MultiFixMessages.ExplicitEncodingCleanUp_description,new Object[] {UseExplicitEncodingFixCore.this.toString(), cb.toString()}), cuRewrite);
				cuRewrite.getASTRewrite().setTargetSourceRangeComputer(computer);
				explicitencoding.rewrite(UseExplicitEncodingFixCore.this, visited, cuRewrite, group, cb, data);
			}
		};
	}

	final static TargetSourceRangeComputer computer= new TargetSourceRangeComputer() {
		@Override
		public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
			if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
				return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
			}
			return super.computeSourceRange(nodeWithComment);
		}
	};

	@Override
	public String toString() {
		return explicitencoding.toString();
	}
}