Input:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.ui.PlatformUI;
import com.abap.sql.beautifier.preferences.PreferenceConstants;
import com.abap.sql.beautifier.settings.AbstractSqlSetting;
import com.abap.sql.beautifier.settings.CommentsAdder;
import com.abap.sql.beautifier.settings.ConditionAligner;
import com.abap.sql.beautifier.settings.JoinCombiner;
import com.abap.sql.beautifier.settings.OperatorUnifier;
import com.abap.sql.beautifier.settings.Restructor;
import com.abap.sql.beautifier.settings.SelectCombiner;
import com.abap.sql.beautifier.settings.SpaceAdder;
import com.abap.sql.beautifier.statement.AbapSql;
import com.abap.sql.beautifier.utility.BeautifierIcon;
import com.abap.sql.beautifier.utility.Utility;
import com.sap.adt.tools.abapsource.ui.AbapSourceUi;
import com.sap.adt.tools.abapsource.ui.IAbapSourceUi;
import com.sap.adt.tools.abapsource.ui.sources.IAbapSourceScannerServices;
import com.sap.adt.tools.abapsource.ui.sources.IAbapSourceScannerServices.Token;
import com.sap.adt.tools.abapsource.ui.sources.editors.AbapSourcePage;

Context:
ABAP SQL Beautifier/src/com/abap/sql/beautifier/StatementProcessor.java
package com.abap.sql.beautifier;

public class StatementProcessor implements IQuickAssistProcessor {

	public IDocument document = null;
	public IAbapSourceScannerServices scannerServices = null;
	public AbapSourcePage sourcePage;
	public IAbapSourceUi sourceUi = null;
	private String sql = "";
	private String code;
	private int diff;
	private int end;
	private int offsetCursor;
	private int startReplacement;
	private boolean oldSyntax = true;

	private String beautifyStatement(String inputCode) {
		// otherwise the whole beautifier would not work
		inputCode = inputCode.toUpperCase();

ABAP SQL Beautifier/src/com/abap/sql/beautifier/preferences/PreferenceConstants.java
public class PreferenceConstants {

	public static final String ALLIGN_OPERS = "ALLIGN_OPERS";

	public static final String COMBINE_CHAR_LIMIT = "COMBINE_CHAR_LIMIT";

	public static final String COMBINE_SMALL_SELECT = "COMBINE_SMALL_SELECT";

	public static final String COMMAS = "COMMAS";

	public static final String ESCAPING = "ESCAPING";

	public static final String OPERSTYLE = "OPERSTYLE";

	public static final String ORDER_OLD_SYNTAX = "ORDER_OLD_SYNTAX";

	public static final String ORDER_NEW_SYNTAX = "ORDER_NEW_SYNTAX";

	public static final String TAB_ALL = "TAB_ALL";

	public static final String TAB_CONDITIONS = "TAB_CONDITIONS";

	public static final String LINE_CHAR_LIMIT = "LINE_CHAR_LIMIT";

	public static final String COMBINE_SMALL_JOINS_LIMIT = "COMBINE_SMALL_JOINS_LIMIT";

	public static final String COMBINE_SMALL_JOINS = "COMBINE_SMALL_JOINS";

	public static final String TAB_CONDITIONS_NEW_SYNTAX = "TAB_CONDITIONS_NEW_SYNTAX";

	public static final String TAB_ALL_NEW_SYNTAX = "TAB_ALL_NEW_SYNTAX";

	public static final String COMMENTSTYLE = "COMMENTSTYLE";

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/JoinCombiner.java
public class JoinCombiner extends AbstractSqlSetting {

	public JoinCombiner() {

	}

	@Override
	public void apply() {
		if (abapSql.isOldSyntax()) {
			// TODO enable also for new syntax

			if (Activator.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.COMBINE_SMALL_JOINS)) {

				int limit = Activator.getDefault().getPreferenceStore()
						.getInt(PreferenceConstants.COMBINE_SMALL_JOINS_LIMIT);

				AbapSqlPart fromPart = abapSql.getPart(Abap.FROM);

				List<String> fromLines = fromPart.getLines();

				String fromLine = "";

				for (String line : fromLines) {
					if(fromLine.trim().equals("")) {
						fromLine = fromLine + line;
					} else {
						fromLine = fromLine + " " + line;
					}

					if (fromLine.length() > limit) {
						// too big, not possible
						return;
					}
				}
				fromLines = new ArrayList<String>();
				fromLines.add(fromLine);
				fromPart.setLines(fromLines);
				abapSql.setPart(Abap.FROM, fromPart);
			}

		}
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/CommentsAdder.java
public class CommentsAdder extends AbstractSqlSetting {
	private String commentStyle = "";

	public CommentsAdder() {

	}

	@Override
	public void apply() {

		// apply comments
		commentStyle = Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.COMMENTSTYLE);

		if (abapSql.getComments().isEmpty()) {
			return;
		}

		if (commentStyle.equals("UP") || commentStyle.equals("DOWN")) {

			setCommentsToOrder();

			addFirstLineWhite();

			addComments();

		}

	}

	private void addFirstLineWhite() {
		AbapSqlPart part = abapSql.getPart(Abap.SELECT);
		String name;
		if (part == null) {
			part = abapSql.getPart(Abap.SELECTFROM);
			name = Abap.SELECTFROM;
			if (part == null) {
				part = abapSql.getPart(Abap.SELECT_SINGLE_FROM);
				name = Abap.SELECT_SINGLE_FROM;
			}
		} else {
			name = Abap.SELECT;
		}

		List<String> lines = part.getLines();

		// add start white and replace it
		String firstLine = abapSql.getStartWhite() + lines.get(0);
		lines.set(0, firstLine);
		part.setLines(lines);

		abapSql.setPart(name, part);
	}

	private void addComments() {
		List<String> comments = abapSql.getComments();
		String firstComment = comments.get(0);
		// check if first comment is full line comment
		if (firstComment.startsWith("*")) {
			// add new line
			firstComment = "\r\n" + firstComment;
			comments.set(0, firstComment);
		}

		AbapSqlPart commentPart = Factory.getPartObject(Abap.COMMENT, comments);

		abapSql.setPart(Abap.COMMENT, commentPart);

	}

	private void setCommentsToOrder() {

		List<String> order = abapSql.getOrder();

		// set comments position
		if (commentStyle.equals("UP")) {
			order.add(0, Abap.COMMENT);
		} else if (commentStyle.equals("DOWN")) {
			order.add(order.size() - 1, Abap.COMMENT);
		}

		abapSql.setOrder(order);
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/ConditionAligner.java
public class ConditionAligner extends AbstractSqlSetting {

	public ConditionAligner() {
	}

	private List<String> alignOperators(List<String> sqlConditions) {
		sqlConditions = deleteUnnecessaryEmptyChars(sqlConditions);

		int curIndex = 0;
		int maxIndex = getMaxIndexOfOperator(sqlConditions);

		StringBuilder sb;

		boolean shouldAlignOneCharOpers = !validateOpersSameLength(sqlConditions);

		List<String> returnLines = new ArrayList<>();

		for (String line : sqlConditions) {

			sb = new StringBuilder(line);

			curIndex = getIndexOfOper(line);

			// if no oper found --> curIndex = -1
			if (curIndex >= 0) {

				curIndex = 0;
				while (curIndex < maxIndex) {

					curIndex = getIndexOfOper(line);

					// if still below max index, insert empty char
					if (curIndex < maxIndex) {
						line = sb.insert(curIndex, " ").toString();
					}

				}
			}

			if (shouldAlignOneCharOpers) {
				for (String oper : Utility.getOneCharOpers()) {
					if (line.contains(oper)) {
						line = sb.insert(curIndex + 2, " ").toString();
						break;
					}
				}
			}

			returnLines.add(line);

		}

		return returnLines;
	}

	@Override
	public void apply() {

		boolean shouldAlign = Activator.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.ALLIGN_OPERS);

		if (shouldAlign) {

			AbapSqlPart wherePart = abapSql.getPart(Abap.WHERE);

			if (wherePart != null) {
				List<String> wherePartLines = wherePart.getLines();
				wherePartLines = alignOperators(wherePartLines);
				wherePart.setLines(wherePartLines);
				abapSql.setPart(Abap.WHERE, wherePart);
			}

			AbapSqlPart fromPart = abapSql.getPart(Abap.FROM);

			if (fromPart != null) {
				List<String> fromPartLines = fromPart.getLines();
				fromPartLines = alignOperators(fromPartLines);
				fromPart.setLines(fromPartLines);
				abapSql.setPart(Abap.FROM, fromPart);
			}

		}

	}

	private List<String> deleteUnnecessaryEmptyChars(List<String> sqlConditions) {
		// delete tabs and unnecessary empty chars
		for (int i = 0; i < sqlConditions.size(); i++) {
			String curLine = sqlConditions.get(i);
			curLine = curLine.replace("\t", "");
			sqlConditions.set(i, curLine);
		}

		return sqlConditions;
	}

	private int getIndexOfOper(String line) {
		List<String> opers = Utility.getAllOperators();
		for (String oper : opers) {
			int index = line.indexOf(oper);
			index = line.lastIndexOf(oper);
			if (index != -1) {
				// oper found
				return index;
			}
		}
		return -1;
	}

	private int getMaxIndexOfOperator(List<String> sqlConditions) {
		int curIndex = 0;
		int maxIndex = 0;
		for (String line : sqlConditions) {
			curIndex = getIndexOfOper(line);
			if (curIndex > maxIndex) {
				maxIndex = curIndex;
			}
		}

		return maxIndex;

	}

	// returns true if all opers are having the same length (2 or 1)
	private boolean validateOpersSameLength(List<String> sqlConditions) {
		boolean containsOne = false;
		boolean containsTwo = false;
		for (String line : sqlConditions) {
			for (String oper : Utility.getOneCharOpers()) {
				if (line.contains(oper)) {
					containsOne = true;
					break;
				}
			}
			for (String oper : Utility.getTwoCharOpers()) {
				if (line.contains(oper)) {
					containsTwo = true;
					break;
				}
			}
		}
		if (containsOne == containsTwo) {
			// contains both
			return false;
		}
		// same length
		return true;
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/statement/AbapSql.java
public class AbapSql {

	private Map<String, AbapSqlPart> parts;
	private List<String> order;
	private boolean isOldSyntax = true;
	private List<String> comments = new ArrayList<String>();
	private String startWhite = "";

	public AbapSql(String sql, int diff) {
		startWhite = Utility.getWhiteChars(diff);

		parts = new HashMap<String, AbapSqlPart>();

		// get order
		isOldSyntax = checkIfOldSyntax(sql);
		if (isOldSyntax) {
			setOrder(buildOrder(PreferenceConstants.ORDER_OLD_SYNTAX));
		} else {
			setOrder(buildOrder(PreferenceConstants.ORDER_NEW_SYNTAX));
		}

		appendSecWords();

		sql = filterComments(sql);

		sql = Utility.cleanString(sql);

		setFromString(sql);

		resetFormat();

	}

	private void appendSecWords() {
		// append keywords, which are introducing the same part
		int index;

		// INTO ~ APPENDING
		index = order.indexOf(Abap.INTO);
		if (index != -1) {
			order.add(index, Abap.APPENDING);
		}

		// UPTO ~ OFFSET
		index = order.indexOf(Abap.UPTO);
		if (index != -1) {
			order.add(index, Abap.OFFSET);
		}

		// INTO ~ INTO CORRESPONDING FIELDS OF
		index = order.indexOf(Abap.INTO);
		if (index != -1) {
			order.add(index, Abap.INTO_COR_FI_OF);
		}

	}

	private String filterComments(String sql) {
		String returnSql = "";
		String asterisks = "*";
		String apostrophes = "\"";

		if (sql.contains(asterisks) || sql.contains(apostrophes)) {
			List<String> lines = Arrays.asList(sql.split("\r\n"));

			for (String line : lines) {
				if (line.contains(asterisks) || line.contains(apostrophes)) {
					int pos = line.indexOf(asterisks);

					// check if asterisks is first char --> full line comment
					if (pos == 0) {
						comments.add(line.trim());
						continue;
					}

					// asterisks was not a comment --> check apostrophes
					pos = line.indexOf(apostrophes);

					if (pos == -1) {
						returnSql = returnSql + line + "\r\n";
					} else {
						comments.add(line.substring(pos).trim());
						returnSql = returnSql + line.substring(0, pos) + "\r\n";
					}

				} else {
					returnSql = returnSql + line + "\r\n";
				}

			}

			return returnSql;

		} else {
			// does not contain any comments, return original
			return sql;
		}

	}

	private List<String> buildOrder(String syntaxType) {
		List<String> returnOrder = new ArrayList<>();

		String curOrderString = Activator.getDefault().getPreferenceStore().getString(syntaxType);

		List<String> orderSplit = Arrays.asList(curOrderString.split(","));

		for (String keyword : orderSplit) {

			keyword = keyword.trim();

			//add additional select single
			if(this.isOldSyntax == false && keyword.equals(Abap.SELECTFROM)) {
				returnOrder.add(Abap.SELECT_SINGLE_FROM);
			}

			// ignore empty values
			if (keyword != "") {
				returnOrder.add(keyword.toUpperCase());
			}
		}

		return returnOrder;
	}

	public List<String> getOrder() {
		return order;
	}

	public void setOrder(List<String> order) {
		this.order = order;
	}

	public AbapSqlPart getPart(String partname) {

		AbapSqlPart part = parts.get(partname);

		if (partname == Abap.FROM && part == null) {
			part = parts.get(Abap.SELECTFROM);
		} else if (partname == Abap.INTO && part == null) {
			part = parts.get(Abap.APPENDING);
		}

		return part;

	}

	public void setPart(String partname, AbapSqlPart part) {

		parts.put(partname, part);

	}

	public boolean isOldSyntax() {
//		String firstLine = getPart(Abap.SELECT).getLines().get(0);
//
//
//		List<String> tokens = Arrays.asList(firstLine.trim().split(" "));
//
//		if (tokens.size() >= 2) {
//			if (tokens.get(0).equals(Abap.FROM) && tokens.get(1).equals(Abap.FROM)) {
//				return false;
//			}
//		}
		AbapSqlPart part = getPart(Abap.FROM);

		if (part == null) {
			part = getPart(Abap.SELECTFROM);
		}
		String firstLine = "";
		List<String> tokens;

		try {
			firstLine = part.getLines().get(0);
		} catch (IndexOutOfBoundsException | NullPointerException ex) {
			// try other method to check syntax
			part = getPart(Abap.SELECT);
			if (part == null) {
				part = getPart(Abap.SELECTFROM);
				if (part == null) {
					part = getPart(Abap.SELECT_SINGLE_FROM);
				}
			}
			firstLine = part.getLines().get(0).trim();

			tokens = Arrays.asList(firstLine.trim().split(" "));

			String firstToken = tokens.get(0);
			String secToken = tokens.get(1);
			if (firstToken.equals(Abap.SELECT) && secToken.equals(Abap.FROM)) {
				return false;
			} else {
				return true;
			}

		}

		tokens = Arrays.asList(firstLine.trim().split(" "));

		if (tokens.get(0).equals(Abap.SELECT)) {
			return false;
		} else {
			return true;
		}

	}

	public List<String> splitSql(String sql, List<String> keywords) {
		List<String> splits = new ArrayList<>();
		List<String> result = new ArrayList<>();

		String pattern = "\\b(" + String.join("|", keywords) + ")\\b";

		Pattern regex = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
		Matcher matcher = regex.matcher(sql);

		int lastIndex = 0;

		String curString = "";

		while (matcher.find()) {
			int mStart = matcher.start();

			int mEnd = matcher.end();

			String keyword = sql.substring(mStart, mEnd).trim();

			// verify no literal
			boolean isLiteral = Utility.isLiteral(sql, mStart, mEnd);

			if (isLiteral) {
				// save cur statement
				curString = curString + sql.substring(lastIndex, mEnd);
				lastIndex = mEnd;
				continue;
			}

			if (!curString.matches("")) {
				// cur statement not empty, add to list
				curString = curString + sql.substring(lastIndex, mStart);
				splits.add(curString.trim());
				curString = "";
			}

			if (lastIndex < mStart) {
				splits.add(sql.substring(lastIndex, mStart).trim());
			}

			splits.add(sql.substring(mStart, mEnd).trim());

			lastIndex = mEnd;
		}

		// add remaining text after matching last keyword
		if (lastIndex < sql.length()) {
			splits.add(sql.substring(lastIndex).trim());
		}

		// combine keyword + rest
		String curLine = "";
		for (int i = 0; i < splits.size(); i++) {
			if (i % 2 == 0) {
				curLine = splits.get(i);

			} else {
				curLine += " " + splits.get(i);
				result.add(curLine);
			}
		}

		// sorting
		result.sort(Comparator.comparingInt(s -> getFirstMatchKeyIndex(s, keywords)));

		return result;
	}

	private int getFirstMatchKeyIndex(String string, List<String> keywords) {
		for (int i = 0; i < keywords.size(); i++) {
			String keyword = keywords.get(i);
			if (string.contains(keyword)) {
				return i;
			}
		}
		return keywords.size();
	}

	public void resetFormat() {
//		String sign = "\"";

		// clean all lines in all parts = reset format
		for (String keyword : getOrder()) {

			AbapSqlPart curPart = parts.get(keyword);

			if (parts.get(keyword) != null) {

				List<String> curLines = curPart.getLines();

				List<String> newLines = new ArrayList<String>();

				for (String line : curLines) {

//					if (line.contains(sign)) {
//						int pos = line.indexOf(sign);
//
//						comments.add(line.substring(pos));
//
//						line = line.substring(0, pos);
//					}

					line = Utility.deleteSpaces(line).trim();

					newLines.add(line);
				}

				setPart(keyword, curPart);
			}
		}

	}

	public void setFromString(String sql) {
		sql = Utility.cleanString(sql);

		if (sql.endsWith(".")) {
			// delete point
			sql = sql.substring(0, sql.length() - 1);
		}

		List<String> parts = splitSql(sql, getOrder());
		List<String> remainingParts = new ArrayList<>();

		for (String keyword : getOrder()) {
			for (String split : parts) {
				if (split.trim().startsWith(keyword)) {
					List<String> part = Arrays.asList(split.split("\r\n"));

					// without starting spaces
					List<String> finalPartList = new ArrayList<>();
					for (String line : part) {
						finalPartList.add(line.trim());
					}

					AbapSqlPart finalPart = Factory.getPartObject(keyword, finalPartList);
					setPart(keyword, finalPart);

					parts.remove(split);

					break;
				}

			}
		}

		verifyOrder();

	}

	public static boolean checkIfOldSyntax(String sql) {
		sql = Utility.cleanString(sql);
		List<String> splits = Arrays.asList(sql.split(" "));
		if (splits.size() > 2) {

			String secToken = splits.get(1).toString().toUpperCase();
			String thirdToken = splits.get(2).toString().toUpperCase();

			if (secToken.equals(Abap.FROM) || (secToken.equals(Abap.SINGLE) && thirdToken.equals(Abap.FROM))) {
				// new syntax
				return false;
			}

		}
		// oldSyntax
		return true;

	}

	@Override
	public String toString() {
		String sqlString = "";

		int limit = Activator.getDefault().getPreferenceStore().getInt(PreferenceConstants.LINE_CHAR_LIMIT);

		if (limit == 0) {
			// forbidden
			limit = 1;
		}

		if (!isOldSyntax && this.getPart(Abap.FIELDS) == null) {
			convertToNewSyntax();
		}

		for (String keyword : getOrder()) {
			AbapSqlPart curPart = parts.get(keyword);
			if (curPart != null) {
				List<String> curLines = curPart.getLines();
				for (String line : curLines) {

					if (line.length() < limit || keyword.equals(Abap.COMMENT)) {
						// no split on comments
						sqlString = sqlString + line + "\r\n";
					} else {
						// line limit reached
						List<String> splittedLines = Utility.splitLine(line, limit);
						for (String splittedLine : splittedLines) {
							sqlString = sqlString + splittedLine + "\r\n";
						}
					}

				}
			}
		}

		// build return String
		sqlString = sqlString.substring(0, sqlString.length() - "\r\n".length());

		return sqlString;
	}

	public void setPoint() {
		List<String> reverseOrder = new ArrayList<String>(getOrder());
		Collections.reverse(reverseOrder);

		for (String keyword : reverseOrder) {
			if (keyword.equals(Abap.COMMENT)) {
				continue;
			}
			if (containsPart(keyword)) {
				// last part found. set point to the end
				AbapSqlPart lastPart = getPart(keyword);
				List<String> lines = lastPart.getLines();
				int pos = lines.size() - 1;
				String lastLine = lines.get(pos) + ".";
				lines.set(pos, lastLine);

//				lastPart.setLines(lines);
//				setPart(keyword, lastPart);
				return;
			}

		}

	}

	public AbapSql convertToNewSyntax() {
		if (!isOldSyntax && this.getPart(Abap.FIELDS) == null) {

			// transform the from part to 'select' and fields to 'fields'
			AbapSqlPart selectPart = getPart(Abap.SELECT);
			AbapSqlPart fromPart = getPart(Abap.FROM);

			List<String> selectLines = selectPart.getLines();
			List<String> fromLines = fromPart.getLines();

			List<String> newFromLines = new ArrayList<String>();
			List<String> fieldLines = new ArrayList<String>();

			// reset from
			setPart(Abap.FROM, null);

			// build fields
			for (String line : selectLines) {
				if (line.trim().startsWith(Abap.SELECT)) {
					line = line.replaceFirst(Abap.SELECT, "");
				}
				fieldLines.add(line);
			}

			AbapSqlPart fieldsPart = Factory.getPartObject(Abap.FIELDS, fieldLines);
			setPart(Abap.FIELDS, fieldsPart);

			// new select

			for (int i = 0; i < fromLines.size(); i++) {
				String curLine = fromLines.get(i);
				if (i == 0) {
					curLine = Abap.SELECT + curLine;
				}
				newFromLines.add(curLine);
			}

			AbapSqlPart newSelectPart = Factory.getPartObject(Abap.SELECT, newFromLines);
			setPart(Abap.SELECT, newSelectPart);

		}

		return this;

	}

	public void verifyOrder() {
		// deactivated --> not necessary?

//		// check if statement contains FOR ALL ENTRIES and if INTO is at the end
//		if (containsPart(Abap.FORALLENTRIES)) {
//			List<String> curOrder = getOrder();
//
//			if (!curOrder.get(curOrder.size() - 1).equals(Abap.INTO)) {
//				for (int i = 0; i < curOrder.size(); i++) {
//					String keyword = curOrder.get(i);
//
//					if (keyword.equals(Abap.INTO)) {
//						// remove from current position and add it to the end
//						curOrder.remove(i);
//						curOrder.add(keyword);
//						return;
//					}
//
//				}
//
//			}
//
//		}
	}

	public boolean containsPart(String partname) {
		AbapSqlPart part = getPart(partname);

		if (part == null) {
			return false;
		} else {
			return true;
		}
	}

	public String getStartWhite() {
		return startWhite;
	}

	public List<String> getComments() {
		return comments;
	}

	public void setComments(List<String> comments) {
		this.comments = comments;
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/utility/BeautifierIcon.java
public class BeautifierIcon {
	private static Image icon;

	public static Image get() {
		if (icon == null) {
			Activator.getDefault();
			icon = AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/magicwand16.png").createImage();
		}
		return icon;
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/OperatorUnifier.java
public class OperatorUnifier extends AbstractSqlSetting {

	String operStyle;

	public OperatorUnifier() {
	}

	public OperatorUnifier(String operStyle) {
		this.operStyle = operStyle;
	}

	@Override
	public void apply() {
		String operStyle = Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.OPERSTYLE);
		if (operStyle == "sign" || operStyle == "char") {
			for (String keyword : abapSql.getOrder()) {

				AbapSqlPart curPart = abapSql.getPart(keyword);

				if (curPart != null) {
					List<String> curPartLines = curPart.getLines();
					List<String> newPartLines = new ArrayList<>();
					for (String line : curPartLines) {

						if (operStyle == "sign") {
							line = line.replace(" EQ ", " = ");
							line = line.replace(" NE ", " <> ");
							line = line.replace(" LT ", " < ");
							line = line.replace(" GT ", " > ");
							line = line.replace(" LE ", " <= ");
							line = line.replace(" GE ", " >= ");
						} else if (operStyle == "char") {
							line = line.replace(" = ", " EQ ");
							line = line.replace(" <> ", " NE ");
							line = line.replace(" < ", " LT ");
							line = line.replace(" > ", " GT ");
							line = line.replace(" <= ", " LE ");
							line = line.replace(" >= ", " GE ");
						}

						newPartLines.add(line);

					}

					curPart.setLines(newPartLines);

					abapSql.setPart(keyword, curPart);
				}
			}
		}

	}

	public void setOperStyleToChar() {
		operStyle = "char";
	}

	public void setOperStyleToSign() {
		operStyle = "sign";
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/SpaceAdder.java
public class SpaceAdder extends AbstractSqlSetting {

	String emptySpaces = "";

	public SpaceAdder() {

	}

	@Override
	public void apply() {

		// everything
		tabAllLines();

		// conditions
		boolean shouldTabCond;
		boolean oldSyntax = abapSql.isOldSyntax();

		if (oldSyntax) {
			shouldTabCond = Activator.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.TAB_CONDITIONS);
		} else {
			shouldTabCond = Activator.getDefault().getPreferenceStore()
					.getBoolean(PreferenceConstants.TAB_CONDITIONS_NEW_SYNTAX);
		}

		if (shouldTabCond) {

			addSpacesFrom();

			addSpacesWhere();

		}
	}

	private void addSpacesFrom() {
		AbapSqlPart fromPart = abapSql.getPart(Abap.FROM);

		if (fromPart == null) {
			return;
		}

		List<String> fromLines = fromPart.getLines();
		List<String> newFromLines = new ArrayList<>();

		for (String line : fromLines) {
			String newLine = tabConditionLine(line);
			newFromLines.add(newLine);
		}

		fromPart.setLines(newFromLines);

		abapSql.setPart(Abap.FROM, fromPart);

	}

	private void addSpacesWhere() {
		AbapSqlPart wherePart = abapSql.getPart(Abap.WHERE);

		// if no conditions in sql, nothing to do
		if (wherePart == null) {
			return;
		}

		List<String> whereLines = wherePart.getLines();
		List<String> newWhereLines = new ArrayList<>();

		for (String line : whereLines) {
			newWhereLines.add(tabConditionLine(line));
		}

		wherePart.setLines(newWhereLines);

		abapSql.setPart(Abap.WHERE, wherePart);
	}

	private void tabAllLines() {
		// add empty spaces to every line except SELECT depending on preferences
		int emptySpacesInt;

		if (abapSql.isOldSyntax()) {
			emptySpacesInt = Activator.getDefault().getPreferenceStore().getInt(PreferenceConstants.TAB_ALL);
		} else {
			emptySpacesInt = Activator.getDefault().getPreferenceStore().getInt(PreferenceConstants.TAB_ALL_NEW_SYNTAX);
		}

		emptySpaces = Utility.getWhiteChars(emptySpacesInt);

		for (String keyword : abapSql.getOrder()) {
			if (abapSql.getPart(keyword) != null) {

				AbapSqlPart curPart = abapSql.getPart(keyword);
				List<String> curPartLines = curPart.getLines();
				List<String> newLines = new ArrayList<>();

				if (keyword.equalsIgnoreCase(Abap.SELECT) || keyword.equalsIgnoreCase(Abap.SELECTFROM)
						|| keyword.equalsIgnoreCase(Abap.SELECT_SINGLE_FROM)) {
					// exclude first line
					newLines.add(curPartLines.get(0));
					curPartLines.remove(0);
				}

				for (String line : curPartLines) {
					String newLine = abapSql.getStartWhite() + emptySpaces + line; // diff = empty spaces in selected
																					// ABAP Code
					newLines.add(newLine);
				}
				curPart.setLines(newLines);
				abapSql.setPart(keyword, curPart);
			}
		}

	}

	private String tabConditionLine(String line) {
		String newLine;

		// AND --> 1 space less
		if (line.trim().startsWith("AND")) {
			newLine = "  " + line;
		} else if (line.trim().startsWith("OR") || line.trim().startsWith("ON")) {
			newLine = "   " + line;
		} else {
			newLine = line;
		}

		return newLine;
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/utility/Utility.java
public class Utility {

	public final static String placeholder = "@!%&";

	public static int countKeyword(String statement, String keyword) {

		keyword = keyword.toUpperCase();

		int count = 0;

		List<String> cleanTokenList = convertToCleanTokenList(statement);

		for (int i = 0; i < cleanTokenList.size(); i++) {
			String token = cleanTokenList.get(i).trim();
			if (token.equalsIgnoreCase(keyword)) {

				if (i == 0) {
					count++;
					continue;
				}

				if (i < cleanTokenList.size()) {
					String tokenBefore = cleanTokenList.get(i - 1);
					String tokenAfter = cleanTokenList.get(i + 1);

					if (!tokenBefore.trim().matches("'") && !tokenAfter.trim().matches("'")) {
						count++;
					}

				}

			}
		}

		return count;
	}

	public static boolean isLiteral(String statement, int start, int end) {
		String statementStart;

		if (start == 0) {
			statementStart = statement.substring(0, start);
		} else {
			statementStart = statement.substring(0, start - 1);
		}

		String statementMid = statement.substring(statementStart.length(), end + 1) + placeholder + " ";
		String statementEnd = statement.substring(end + 1, statement.length());

		statement = statementStart + statementMid + statementEnd;

		List<String> cleanTokenList = convertToCleanTokenList(statement);

		boolean isLiteral = false;
		int count = 0;

		for (int i = 1; i < cleanTokenList.size(); i++) {

			String curToken = cleanTokenList.get(i);

			// count all " ' "
			if (curToken.matches("'")) {
				count++;
			}

			if (curToken.contains(placeholder)) {

				curToken = curToken.replaceAll(placeholder, "");

				String tokenBefore = cleanTokenList.get(i - 1).trim();
				String tokenAfter = cleanTokenList.get(i + 1).trim();

				boolean startLiteral = (tokenBefore.matches("'") || curToken.startsWith("'"));
				boolean endsLiteral = (tokenAfter.matches("'") || curToken.endsWith("'"));

				if (startLiteral && endsLiteral) {
					if (count % 2 != 0) {
						isLiteral = true;
					}

				}

				break;

			}
		}

		return isLiteral;
	}

	private static List<String> convertToCleanTokenList(String statement) {
		statement = statement.toUpperCase();

		List<String> tokenList = Arrays.asList(statement.trim().split(" "));

		List<String> cleanTokenList = new ArrayList<String>();

		// clean tokenList
		for (String token : tokenList) {
			token = token.replaceAll("\r\n", " ");
			if (!token.isBlank()) {
				cleanTokenList.add(token);
			}
		}

		return cleanTokenList;

	}

	public static String deleteSpaces(String statement) {
		// delete mulitple spaces
		while (statement.contains("  ") || statement.contains("	 ")) {
			statement = statement.replace("  ", " ");
			statement = statement.replace("	", " ");
		}

		return statement;
	}

	public static String deleteLines(String statement) {
		// return statement.replace("\r\n", " ");
		return statement.replace(System.lineSeparator(), " ");
	}

	public static String cleanString(String statement) {
		String cleanedString = deleteLines(statement);
		cleanedString = deleteSpaces(cleanedString);
		return cleanedString;
	}

	public static List<String> getAllOperators() {
		List<String> opers = new ArrayList<>();

		opers.addAll(getTwoCharOpers());

		opers.addAll(getOneCharOpers());

		return opers;
	}

	public static List<String> getOneCharOpers() {
		List<String> opers = new ArrayList<>();

		opers.add(" = ");
		opers.add(" < ");
		opers.add(" > ");

		return opers;

	}

	public static List<String> getTwoCharOpers() {
		List<String> opers = new ArrayList<>();

		opers.add(" EQ ");
		opers.add(" NE ");
		opers.add(" LT ");
		opers.add(" GT ");
		opers.add(" LE ");
		opers.add(" GE ");

		opers.add(" <> ");
		opers.add(" <= ");
		opers.add(" >= ");

		opers.add(" IN ");

		return opers;
	}

	public static List<String> splitLine(String statement, int limit) {
		List<String> lines = new ArrayList<String>();

		int lengthBefore = statement.length();
		statement = statement.strip();
		String whiteChars = Utility.getWhiteChars(lengthBefore - statement.length());
		String whiteCharsTabLines = whiteChars + "  ";

		if (statement.length() > limit) {

			int count = 0;
			int subStrLength = limit;
			while (statement != null) {

				if (statement.length() < subStrLength) {
					subStrLength = statement.length();
				}

				String curLine = statement.substring(0, subStrLength);

				String regex = escapeRegex(statement.trim());

				if (!curLine.matches(regex)) {

					// get index of last space in this line
					int curIndex = curLine.lastIndexOf(" ");

					if (curIndex != -1) {
						// index found
						curLine = curLine.substring(0, curIndex);
					} else {
						// no space found --> find next space
						for (int i = (subStrLength + 1); i < statement.length(); i++) {
							curLine = statement.substring(0, i);
							curIndex = curLine.lastIndexOf(" ");
							if (curIndex != -1) {
								break;
							}
						}

						// still not found? take whole statement
						if (curIndex == -1) {
							curLine = statement;
						}
					}
				}

				if (count == 0) {
					curLine = whiteChars + curLine;
				} else {
					curLine = whiteCharsTabLines + curLine;
				}

				lines.add(curLine);

				regex = escapeRegex(curLine.trim());

				statement = statement.replaceFirst(regex, "").trim();

				if (statement.isBlank()) {
					statement = null;
					break;
				}

				count++;
			}

		} else {
			statement = whiteChars + statement;
			lines.add(statement);
		}

		return lines;

	}

	public static String getWhiteChars(int amount) {
		String white = "";

		for (int i = 0; i < amount; i++) {
			white = white + " ";
		}

		return white;
	}

	public static String escapeRegex(String regex) {
		regex = regex.replace("*", "\\*");
		regex = regex.replace("(", "\\(");
		regex = regex.replace(")", "\\)");

		return regex;
	}

}

ABAP SQL Beautifier/src/com/abap/sql/beautifier/settings/AbstractSqlSetting.java
public abstract class AbstractSqlSetting {

	protected AbapSql abapSql;

	public AbstractSqlSetting() {
	}

	public abstract void apply();

	public AbapSql getAbapSql() {
		return abapSql;
	}

	public void setAbapSql(AbapSql abapSql) {
		this.abapSql = abapSql;
	}

}

Answer: AbapSql abapSql = new AbapSql(inputCode, diff);

<think>
In StatementProcessor.beautifyStatement, after converting the input to upper case the next logical step is to create an AbapSql object with the processed code and the diff value, which will be used by the subsequent settings. The next line therefore constructs the AbapSql instance.
</think>
AbapSql abapSql = new AbapSql(inputCode, diff);


Input:
import com.mangomilk.design_decor.blocks.containers.blue.BlueContainerBlockEntity;
import com.mangomilk.design_decor.blocks.containers.green.GreenContainerBlockEntity;
import com.mangomilk.design_decor.blocks.containers.red.RedContainerBlockEntity;
import com.mangomilk.design_decor.blocks.crushing_wheels.MmbCrushingWheelBlockEntity;
import com.mangomilk.design_decor.blocks.crushing_wheels.MmbCrushingWheelControllerBlockEntity;
import com.mangomilk.design_decor.blocks.gas_tank.GasTankBlockEntity;
import com.mangomilk.design_decor.blocks.industrial_gear.IndustrialGearInstance;
import com.mangomilk.design_decor.blocks.industrial_gear.IndustrialGearRenderer;
import com.mangomilk.design_decor.blocks.millstone.DecoMillStoneBlockEntity;
import com.mangomilk.design_decor.blocks.millstone.instance.*;
import com.mangomilk.design_decor.blocks.millstone.renderer.*;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.kinetics.base.CutoutRotatingInstance;
import com.simibubi.create.content.kinetics.base.KineticBlockEntityRenderer;
import com.simibubi.create.content.kinetics.simpleRelays.BracketedKineticBlockEntity;
import com.tterrag.registrate.util.entry.BlockEntityEntry;
import static com.mangomilk.design_decor.CreateMMBuilding.REGISTRATE;

Context:
src/main/java/com/mangomilk/design_decor/registry/MmbBlockEntities.java
    public static final BlockEntityEntry<BlueContainerBlockEntity> BLUE_CONTAINER = REGISTRATE
            .blockEntity("blue_container", BlueContainerBlockEntity::new)
            .validBlocks(MmbBlocks.RED_CONTAINER)
            .register();

    public static final BlockEntityEntry<MmbCrushingWheelBlockEntity> MMB_CRUSHING_WHEEL = REGISTRATE
            .blockEntity("mmb_crushing_wheel", MmbCrushingWheelBlockEntity::new)
            .instance(() -> CutoutRotatingInstance::new, false)
            .validBlocks(
                    MmbBlocks.GRANITE_CRUSHING_WHEEL,
                    MmbBlocks.DIORITE_CRUSHING_WHEEL,
                    MmbBlocks.LIMESTONE_CRUSHING_WHEEL,
                    MmbBlocks.SCORCHIA_CRUSHING_WHEEL,
                    MmbBlocks.SCORIA_CRUSHING_WHEEL,
                    MmbBlocks.TUFF_CRUSHING_WHEEL,
                    MmbBlocks.VERIDIUM_CRUSHING_WHEEL,
                    MmbBlocks.DRIPSTONE_CRUSHING_WHEEL,
                    MmbBlocks.DEEPSLATE_CRUSHING_WHEEL,
                    MmbBlocks.CRIMSITE_CRUSHING_WHEEL,
                    MmbBlocks.CALCITE_CRUSHING_WHEEL,
                    MmbBlocks.ASURINE_CRUSHING_WHEEL,
                    MmbBlocks.OCHRUM_CRUSHING_WHEEL,
                    AllBlocks.CRUSHING_WHEEL
            )
            .renderer(() -> KineticBlockEntityRenderer::new)
            .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> GRANITE_MILLSTONE =
            REGISTRATE.blockEntity("granite_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> GraniteMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.GRANITE_MILLSTONE)
                    .renderer(() -> GraniteMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> DIORITE_MILLSTONE =
            REGISTRATE.blockEntity("diorite_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> DioriteMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.DIORITE_MILLSTONE)
                    .renderer(() -> DioriteMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> LIMESTONE_MILLSTONE =
            REGISTRATE.blockEntity("limestone_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> LimestoneMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.LIMESTONE_MILLSTONE)
                    .renderer(() -> LimestoneMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> SCORCHIA_MILLSTONE =
            REGISTRATE.blockEntity("scorchia_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> ScorchiaMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.SCORCHIA_MILLSTONE)
                    .renderer(() -> ScorchiaMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> SCORIA_MILLSTONE =
            REGISTRATE.blockEntity("scoria_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> ScoriaMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.SCORIA_MILLSTONE)
                    .renderer(() -> ScoriaMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> TUFF_MILLSTONE =
            REGISTRATE.blockEntity("tuff_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> TuffMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.TUFF_MILLSTONE)
                    .renderer(() -> TuffMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> VERIDIUM_MILLSTONE =
            REGISTRATE.blockEntity("veridium_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> VeridiumMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.VERIDIUM_MILLSTONE)
                    .renderer(() -> VeridiumMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> DRIPSTONE_MILLSTONE =
            REGISTRATE.blockEntity("dripstone_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> DripstoneMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.DRIPSTONE_MILLSTONE)
                    .renderer(() -> DripstoneMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> DEEPSLATE_MILLSTONE =
            REGISTRATE.blockEntity("deepslate_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> DeepslateMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.DEEPSLATE_MILLSTONE)
                    .renderer(() -> DeepslateMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> CRIMSITE_MILLSTONE =
            REGISTRATE.blockEntity("crimsite_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> CrimsiteMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.CRIMSITE_MILLSTONE)
                    .renderer(() -> CrimsiteMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> CALCITE_MILLSTONE =
            REGISTRATE.blockEntity("calcite_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> CalciteMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.CALCITE_MILLSTONE)
                    .renderer(() -> CalciteMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> ASURINE_MILLSTONE =
            REGISTRATE.blockEntity("asurine_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> AsurineMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.ASURINE_MILLSTONE)
                    .renderer(() -> AsurineMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<DecoMillStoneBlockEntity> OCHRUM_MILLSTONE =
            REGISTRATE.blockEntity("ochrum_millstone", DecoMillStoneBlockEntity::new)
                    .instance(() -> OchrumMillStoneCogInstance::new, false)
                    .validBlocks(MmbBlocks.OCHRUM_MILLSTONE)
                    .renderer(() -> OchrumMillStoneRenderer::new)
                    .register();

    public static final BlockEntityEntry<MmbCrushingWheelControllerBlockEntity> MMB_CRUSHING_WHEEL_CONTROLLER = REGISTRATE

src/main/java/com/mangomilk/design_decor/blocks/containers/green/GreenContainerBlockEntity.java
public class GreenContainerBlockEntity extends ItemVaultBlockEntity implements IMultiBlockEntityContainer.Inventory {

	protected LazyOptional<IItemHandler> itemCapability;

	protected ItemStackHandler inventory;
	protected BlockPos controller;
	protected BlockPos lastKnownPos;
	protected boolean updateConnectivity;
	public int radius;
	public int length;
	protected Axis axis;

	public GreenContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
		super(type, pos, state);

		inventory = new ItemStackHandler(AllConfigs.server().logistics.vaultCapacity.get()) {
			@Override
			protected void onContentsChanged(int slot) {
				super.onContentsChanged(slot);
				updateComparators();
			}
		};

		itemCapability = LazyOptional.empty();
		radius = 1;
		length = 1;
	}

	@Override
	public void addBehaviours(List<BlockEntityBehaviour> behaviours) {}

	protected void updateConnectivity() {
		updateConnectivity = false;
		if (level.isClientSide())
			return;
		if (!isController())
			return;
		ConnectivityHandler.formMulti(this);

	}

	protected void updateComparators() {
		GreenContainerBlockEntity controllerBE = getControllerBE();
		if (controllerBE == null)
			return;

		level.blockEntityChanged(controllerBE.worldPosition);

		BlockPos pos = controllerBE.getBlockPos();
		for (int y = 0; y < controllerBE.radius; y++) {
			for (int z = 0; z < (controllerBE.axis == Axis.X ? controllerBE.radius : controllerBE.length); z++) {
				for (int x = 0; x < (controllerBE.axis == Axis.Z ? controllerBE.radius : controllerBE.length); x++) {
					level.updateNeighbourForOutputSignal(pos.offset(x, y, z), getBlockState().getBlock());
				}
			}
		}
	}

	@Override
	public void tick() {
		super.tick();

		if (lastKnownPos == null)
			lastKnownPos = getBlockPos();
		else if (!lastKnownPos.equals(worldPosition) && worldPosition != null) {
			onPositionChanged();
			return;
		}

		if (updateConnectivity)
			updateConnectivity();
	}

	@Override
	public BlockPos getLastKnownPos() {
		return lastKnownPos;
	}

	@Override
	public boolean isController() {
		return controller == null || worldPosition.getX() == controller.getX()
			&& worldPosition.getY() == controller.getY() && worldPosition.getZ() == controller.getZ();
	}

	private void onPositionChanged() {
		removeController(true);
		lastKnownPos = worldPosition;
	}

	@SuppressWarnings("unchecked")
	@Override
	public GreenContainerBlockEntity getControllerBE() {
		if (isController())
			return this;
		BlockEntity blockEntity = level.getBlockEntity(controller);
		if (blockEntity instanceof GreenContainerBlockEntity)
			return (GreenContainerBlockEntity) blockEntity;
		return null;
	}

	public void removeController(boolean keepContents) {
		if (level.isClientSide())
			return;
		updateConnectivity = true;
		controller = null;
		radius = 1;
		length = 1;

		BlockState state = getBlockState();

			if (GreenContainerBlock.isContainer(state)) {
				state = state.setValue(GreenContainerBlock.LARGE, false);
				getLevel().setBlock(worldPosition, state, 22);
			}

		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public void setController(BlockPos controller) {
		if (level.isClientSide && !isVirtual())
			return;
		if (controller.equals(this.controller))
			return;
		this.controller = controller;
		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public BlockPos getController() {
		return isController() ? worldPosition : controller;
	}

	@Override
	protected void read(CompoundTag compound, boolean clientPacket) {
		super.read(compound, clientPacket);

		BlockPos controllerBefore = controller;
		int prevSize = radius;
		int prevLength = length;

		updateConnectivity = compound.contains("Uninitialized");
		controller = null;
		lastKnownPos = null;

		if (compound.contains("LastKnownPos"))
			lastKnownPos = NbtUtils.readBlockPos(compound.getCompound("LastKnownPos"));
		if (compound.contains("Controller"))
			controller = NbtUtils.readBlockPos(compound.getCompound("Controller"));

		if (isController()) {
			radius = compound.getInt("Size");
			length = compound.getInt("Length");
		}

		if (!clientPacket) {
			inventory.deserializeNBT(compound.getCompound("Inventory"));
			return;
		}

		boolean changeOfController =
			controllerBefore == null ? controller != null : !controllerBefore.equals(controller);
		if (hasLevel() && (changeOfController || prevSize != radius || prevLength != length))
			level.setBlocksDirty(getBlockPos(), Blocks.AIR.defaultBlockState(), getBlockState());
	}

	@Override
	protected void write(CompoundTag compound, boolean clientPacket) {
		if (updateConnectivity)
			compound.putBoolean("Uninitialized", true);
		if (lastKnownPos != null)
			compound.put("LastKnownPos", NbtUtils.writeBlockPos(lastKnownPos));
		if (!isController())
			compound.put("Controller", NbtUtils.writeBlockPos(controller));
		if (isController()) {
			compound.putInt("Size", radius);
			compound.putInt("Length", length);
		}

		if (!clientPacket) {
			compound.putString("StorageType", "CombinedInv");
			compound.put("Inventory", inventory.serializeNBT());
		}
	}

	public ItemStackHandler getInventoryOfBlock() {
		return inventory;
	}

	public void applyInventoryToBlock(ItemStackHandler handler) {
		for (int i = 0; i < inventory.getSlots(); i++)
			inventory.setStackInSlot(i, i < handler.getSlots() ? handler.getStackInSlot(i) : ItemStack.EMPTY);
	}

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
		if (isItemHandlerCap(cap)) {
			initCapability();
			return itemCapability.cast();
		}
		return super.getCapability(cap, side);
	}

	private void initCapability() {
		if (itemCapability.isPresent())
			return;
		if (!isController()) {
			GreenContainerBlockEntity controllerBE = getControllerBE();
			if (controllerBE == null)
				return;
			controllerBE.initCapability();
			itemCapability = controllerBE.itemCapability;
			return;
		}

		boolean	alongZ = GreenContainerBlock.getContainerBlockAxis(getBlockState()) == Axis.Z;

		IItemHandlerModifiable[] invs = new IItemHandlerModifiable[length * radius * radius];
		for (int yOffset = 0; yOffset < length; yOffset++) {
			for (int xOffset = 0; xOffset < radius; xOffset++) {
				for (int zOffset = 0; zOffset < radius; zOffset++) {
					BlockPos containerPos = alongZ ? worldPosition.offset(xOffset, zOffset, yOffset)
						: worldPosition.offset(yOffset, xOffset, zOffset);
					GreenContainerBlockEntity containerAt =
						ConnectivityHandler.partAt(MmbBlockEntities.GREEN_CONTAINER.get(), level, containerPos);
					invs[yOffset * radius * radius + xOffset * radius + zOffset] =
						containerAt != null ? containerAt.inventory : new ItemStackHandler();
				}
			}
		}

		CombinedInvWrapper combinedInvWrapper = new CombinedInvWrapper(invs);
		itemCapability = LazyOptional.of(() -> combinedInvWrapper);
	}

	public static int getMaxLength(int radius) {
		return radius * 3;
	}

	@Override
	public void preventConnectivityUpdate() { updateConnectivity = false; }

	@Override
	public void notifyMultiUpdated() {
		BlockState state = this.getBlockState();

			if (GreenContainerBlock.isContainer(state)) { // safety
				level.setBlock(getBlockPos(), state.setValue(GreenContainerBlock.LARGE, radius > 2), 6);
			}

		itemCapability.invalidate();
		setChanged();
	}

	@Override
	public Axis getMainConnectionAxis() { return getMainAxisOf(this); }

	@Override
	public int getMaxLength(Axis longAxis, int width) {
		if (longAxis == Axis.Y) return getMaxWidth();
		return getMaxLength(width);
	}

	@Override
	public int getMaxWidth() {
		return 3;
	}

	@Override
	public int getHeight() { return length; }

	@Override
	public int getWidth() { return radius; }

	@Override
	public void setHeight(int height) { this.length = height; }

	@Override
	public void setWidth(int width) { this.radius = width; }

	@Override
	public boolean hasInventory() { return true; }
}

src/main/java/com/mangomilk/design_decor/blocks/containers/red/RedContainerBlockEntity.java
public class RedContainerBlockEntity extends ItemVaultBlockEntity implements IMultiBlockEntityContainer.Inventory {

	protected LazyOptional<IItemHandler> itemCapability;

	protected ItemStackHandler inventory;
	protected BlockPos controller;
	protected BlockPos lastKnownPos;
	protected boolean updateConnectivity;
	public int radius;
	public int length;
	protected Axis axis;

	public RedContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
		super(type, pos, state);

		inventory = new ItemStackHandler(AllConfigs.server().logistics.vaultCapacity.get()) {
			@Override
			protected void onContentsChanged(int slot) {
				super.onContentsChanged(slot);
				updateComparators();
			}
		};

		itemCapability = LazyOptional.empty();
		radius = 1;
		length = 1;
	}

	@Override
	public void addBehaviours(List<BlockEntityBehaviour> behaviours) {}

	protected void updateConnectivity() {
		updateConnectivity = false;
		if (level.isClientSide())
			return;
		if (!isController())
			return;
		ConnectivityHandler.formMulti(this);

	}

	protected void updateComparators() {
		RedContainerBlockEntity controllerBE = getControllerBE();
		if (controllerBE == null)
			return;

		level.blockEntityChanged(controllerBE.worldPosition);

		BlockPos pos = controllerBE.getBlockPos();
		for (int y = 0; y < controllerBE.radius; y++) {
			for (int z = 0; z < (controllerBE.axis == Axis.X ? controllerBE.radius : controllerBE.length); z++) {
				for (int x = 0; x < (controllerBE.axis == Axis.Z ? controllerBE.radius : controllerBE.length); x++) {
					level.updateNeighbourForOutputSignal(pos.offset(x, y, z), getBlockState().getBlock());
				}
			}
		}
	}

	@Override
	public void tick() {
		super.tick();

		if (lastKnownPos == null)
			lastKnownPos = getBlockPos();
		else if (!lastKnownPos.equals(worldPosition) && worldPosition != null) {
			onPositionChanged();
			return;
		}

		if (updateConnectivity)
			updateConnectivity();
	}

	@Override
	public BlockPos getLastKnownPos() {
		return lastKnownPos;
	}

	@Override
	public boolean isController() {
		return controller == null || worldPosition.getX() == controller.getX()
			&& worldPosition.getY() == controller.getY() && worldPosition.getZ() == controller.getZ();
	}

	private void onPositionChanged() {
		removeController(true);
		lastKnownPos = worldPosition;
	}

	@SuppressWarnings("unchecked")
	@Override
	public RedContainerBlockEntity getControllerBE() {
		if (isController())
			return this;
		BlockEntity blockEntity = level.getBlockEntity(controller);
		if (blockEntity instanceof RedContainerBlockEntity)
			return (RedContainerBlockEntity) blockEntity;
		return null;
	}

	public void removeController(boolean keepContents) {
		if (level.isClientSide())
			return;
		updateConnectivity = true;
		controller = null;
		radius = 1;
		length = 1;

		BlockState state = getBlockState();

			if (RedContainerBlock.isContainer(state)) {
				state = state.setValue(RedContainerBlock.LARGE, false);
				getLevel().setBlock(worldPosition, state, 22);
			}

		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public void setController(BlockPos controller) {
		if (level.isClientSide && !isVirtual())
			return;
		if (controller.equals(this.controller))
			return;
		this.controller = controller;
		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public BlockPos getController() {
		return isController() ? worldPosition : controller;
	}

	@Override
	protected void read(CompoundTag compound, boolean clientPacket) {
		super.read(compound, clientPacket);

		BlockPos controllerBefore = controller;
		int prevSize = radius;
		int prevLength = length;

		updateConnectivity = compound.contains("Uninitialized");
		controller = null;
		lastKnownPos = null;

		if (compound.contains("LastKnownPos"))
			lastKnownPos = NbtUtils.readBlockPos(compound.getCompound("LastKnownPos"));
		if (compound.contains("Controller"))
			controller = NbtUtils.readBlockPos(compound.getCompound("Controller"));

		if (isController()) {
			radius = compound.getInt("Size");
			length = compound.getInt("Length");
		}

		if (!clientPacket) {
			inventory.deserializeNBT(compound.getCompound("Inventory"));
			return;
		}

		boolean changeOfController =
			controllerBefore == null ? controller != null : !controllerBefore.equals(controller);
		if (hasLevel() && (changeOfController || prevSize != radius || prevLength != length))
			level.setBlocksDirty(getBlockPos(), Blocks.AIR.defaultBlockState(), getBlockState());
	}

	@Override
	protected void write(CompoundTag compound, boolean clientPacket) {
		if (updateConnectivity)
			compound.putBoolean("Uninitialized", true);
		if (lastKnownPos != null)
			compound.put("LastKnownPos", NbtUtils.writeBlockPos(lastKnownPos));
		if (!isController())
			compound.put("Controller", NbtUtils.writeBlockPos(controller));
		if (isController()) {
			compound.putInt("Size", radius);
			compound.putInt("Length", length);
		}

		if (!clientPacket) {
			compound.putString("StorageType", "CombinedInv");
			compound.put("Inventory", inventory.serializeNBT());
		}
	}

	public ItemStackHandler getInventoryOfBlock() {
		return inventory;
	}

	public void applyInventoryToBlock(ItemStackHandler handler) {
		for (int i = 0; i < inventory.getSlots(); i++)
			inventory.setStackInSlot(i, i < handler.getSlots() ? handler.getStackInSlot(i) : ItemStack.EMPTY);
	}

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
		if (isItemHandlerCap(cap)) {
			initCapability();
			return itemCapability.cast();
		}
		return super.getCapability(cap, side);
	}

	private void initCapability() {
		if (itemCapability.isPresent())
			return;
		if (!isController()) {
			RedContainerBlockEntity controllerBE = getControllerBE();
			if (controllerBE == null)
				return;
			controllerBE.initCapability();
			itemCapability = controllerBE.itemCapability;
			return;
		}

		boolean	alongZ = RedContainerBlock.getContainerBlockAxis(getBlockState()) == Axis.Z;

		IItemHandlerModifiable[] invs = new IItemHandlerModifiable[length * radius * radius];
		for (int yOffset = 0; yOffset < length; yOffset++) {
			for (int xOffset = 0; xOffset < radius; xOffset++) {
				for (int zOffset = 0; zOffset < radius; zOffset++) {
					BlockPos containerPos = alongZ ? worldPosition.offset(xOffset, zOffset, yOffset)
						: worldPosition.offset(yOffset, xOffset, zOffset);
					RedContainerBlockEntity containerAt =
						ConnectivityHandler.partAt(MmbBlockEntities.RED_CONTAINER.get(), level, containerPos);
					invs[yOffset * radius * radius + xOffset * radius + zOffset] =
						containerAt != null ? containerAt.inventory : new ItemStackHandler();
				}
			}
		}

		CombinedInvWrapper combinedInvWrapper = new CombinedInvWrapper(invs);
		itemCapability = LazyOptional.of(() -> combinedInvWrapper);
	}

	public static int getMaxLength(int radius) {
		return radius * 3;
	}

	@Override
	public void preventConnectivityUpdate() { updateConnectivity = false; }

	@Override
	public void notifyMultiUpdated() {
		BlockState state = this.getBlockState();

			if (RedContainerBlock.isContainer(state)) { // safety
				level.setBlock(getBlockPos(), state.setValue(RedContainerBlock.LARGE, radius > 2), 6);
			}

		itemCapability.invalidate();
		setChanged();
	}

	@Override
	public Axis getMainConnectionAxis() { return getMainAxisOf(this); }

	@Override
	public int getMaxLength(Axis longAxis, int width) {
		if (longAxis == Axis.Y) return getMaxWidth();
		return getMaxLength(width);
	}

	@Override
	public int getMaxWidth() {
		return 3;
	}

	@Override
	public int getHeight() { return length; }

	@Override
	public int getWidth() { return radius; }

	@Override
	public void setHeight(int height) { this.length = height; }

	@Override
	public void setWidth(int width) { this.radius = width; }

	@Override
	public boolean hasInventory() { return true; }
}

src/main/java/com/mangomilk/design_decor/CreateMMBuilding.java
public static final CreateRegistrate REGISTRATE = CreateRegistrate.create(CreateMMBuilding.MOD_ID).creativeModeTab(()-> MmbCreativeModeTab.BUILDING);

src/main/java/com/mangomilk/design_decor/blocks/millstone/DecoMillStoneBlockEntity.java
public class DecoMillStoneBlockEntity extends MillstoneBlockEntity {
    public DecoMillStoneBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
        super(type, pos, state);
        capability = LazyOptional.of(DecoMillstoneInventoryHandler::new);
    }

    public MillingRecipe lastRecipe;

    @Override
    public void tick() {
        super.tick();

        if (getSpeed() == 0)
            return;
        for (int i = 0; i < outputInv.getSlots(); i++)
            if (outputInv.getStackInSlot(i)
                    .getCount() == outputInv.getSlotLimit(i))
                return;

        if (timer > 0) {
            timer -= getProcessingSpeed();

            if (level.isClientSide) {
                spawnParticles();
                return;
            }
            if (timer <= 0)
                process();
            return;
        }

        if (inputInv.getStackInSlot(0)
                .isEmpty())
            return;

        RecipeWrapper inventoryIn = new RecipeWrapper(inputInv);
        if (lastRecipe == null || !lastRecipe.matches(inventoryIn, level)) {
            Optional<MillingRecipe> recipe = AllRecipeTypes.MILLING.find(inventoryIn, level);
            if (!recipe.isPresent()) {
                timer = 100;
                sendData();
            } else {
                lastRecipe = recipe.get();
                timer = lastRecipe.getProcessingDuration();
                sendData();
            }
            return;
        }

        timer = lastRecipe.getProcessingDuration();
        sendData();
    }

    public void process() {
        RecipeWrapper inventoryIn = new RecipeWrapper(inputInv);

        if (lastRecipe == null || !lastRecipe.matches(inventoryIn, level)) {
            Optional<MillingRecipe> recipe = AllRecipeTypes.MILLING.find(inventoryIn, level);
            if (!recipe.isPresent())
                return;
            lastRecipe = recipe.get();
        }

        ItemStack stackInSlot = inputInv.getStackInSlot(0);
        stackInSlot.shrink(1);
        inputInv.setStackInSlot(0, stackInSlot);
        lastRecipe.rollResults()
                .forEach(stack -> ItemHandlerHelper.insertItemStacked(outputInv, stack, false));
        award(AllAdvancements.MILLSTONE);

        sendData();
        setChanged();
    }

    public boolean canProcess(ItemStack stack) {
        ItemStackHandler tester = new ItemStackHandler(1);
        tester.setStackInSlot(0, stack);
        RecipeWrapper inventoryIn = new RecipeWrapper(tester);

        if (lastRecipe != null && lastRecipe.matches(inventoryIn, level))
            return true;
        return AllRecipeTypes.MILLING.find(inventoryIn, level)
                .isPresent();
    }

    public class DecoMillstoneInventoryHandler extends CombinedInvWrapper {

        public DecoMillstoneInventoryHandler() {
            super(inputInv, outputInv);
        }

        @Override
        public boolean isItemValid(int slot, ItemStack stack) {
            if (outputInv == getHandlerFromIndex(getIndexForSlot(slot)))
                return false;
            return canProcess(stack) && super.isItemValid(slot, stack);
        }

        @Override
        public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
            if (outputInv == getHandlerFromIndex(getIndexForSlot(slot)))
                return stack;
            if (!isItemValid(slot, stack))
                return stack;
            return super.insertItem(slot, stack, simulate);
        }

        @Override
        public ItemStack extractItem(int slot, int amount, boolean simulate) {
            if (inputInv == getHandlerFromIndex(getIndexForSlot(slot)))
                return ItemStack.EMPTY;
            return super.extractItem(slot, amount, simulate);
        }

    }
}

src/main/java/com/mangomilk/design_decor/blocks/crushing_wheels/MmbCrushingWheelBlockEntity.java
@EventBusSubscriber
public class MmbCrushingWheelBlockEntity extends CrushingWheelBlockEntity {

	public static final DamageSource DAMAGE_SOURCE = new DamageSource("create.crush").bypassArmor()
			.setScalesWithDifficulty();

	public MmbCrushingWheelBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
		super(type, pos, state);
		setLazyTickRate(20);
	}

	@Override
	protected AABB createRenderBoundingBox() {
		return new AABB(worldPosition).inflate(1);
	}
}

src/main/java/com/mangomilk/design_decor/blocks/containers/blue/BlueContainerBlockEntity.java
public class BlueContainerBlockEntity extends ItemVaultBlockEntity implements IMultiBlockEntityContainer.Inventory {

	protected LazyOptional<IItemHandler> itemCapability;

	protected ItemStackHandler inventory;
	protected BlockPos controller;
	protected BlockPos lastKnownPos;
	protected boolean updateConnectivity;
	public int radius;
	public int length;
	protected Axis axis;

	public BlueContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
		super(type, pos, state);

		inventory = new ItemStackHandler(AllConfigs.server().logistics.vaultCapacity.get()) {
			@Override
			protected void onContentsChanged(int slot) {
				super.onContentsChanged(slot);
				updateComparators();
			}
		};

		itemCapability = LazyOptional.empty();
		radius = 1;
		length = 1;
	}

	@Override
	public void addBehaviours(List<BlockEntityBehaviour> behaviours) {}

	protected void updateConnectivity() {
		updateConnectivity = false;
		if (level.isClientSide())
			return;
		if (!isController())
			return;
		ConnectivityHandler.formMulti(this);

	}

	protected void updateComparators() {
		BlueContainerBlockEntity controllerBE = getControllerBE();
		if (controllerBE == null)
			return;

		level.blockEntityChanged(controllerBE.worldPosition);

		BlockPos pos = controllerBE.getBlockPos();
		for (int y = 0; y < controllerBE.radius; y++) {
			for (int z = 0; z < (controllerBE.axis == Axis.X ? controllerBE.radius : controllerBE.length); z++) {
				for (int x = 0; x < (controllerBE.axis == Axis.Z ? controllerBE.radius : controllerBE.length); x++) {
					level.updateNeighbourForOutputSignal(pos.offset(x, y, z), getBlockState().getBlock());
				}
			}
		}
	}

	@Override
	public void tick() {
		super.tick();

		if (lastKnownPos == null)
			lastKnownPos = getBlockPos();
		else if (!lastKnownPos.equals(worldPosition) && worldPosition != null) {
			onPositionChanged();
			return;
		}

		if (updateConnectivity)
			updateConnectivity();
	}

	@Override
	public BlockPos getLastKnownPos() {
		return lastKnownPos;
	}

	@Override
	public boolean isController() {
		return controller == null || worldPosition.getX() == controller.getX()
			&& worldPosition.getY() == controller.getY() && worldPosition.getZ() == controller.getZ();
	}

	private void onPositionChanged() {
		removeController(true);
		lastKnownPos = worldPosition;
	}

	@SuppressWarnings("unchecked")
	@Override
	public BlueContainerBlockEntity getControllerBE() {
		if (isController())
			return this;
		BlockEntity blockEntity = level.getBlockEntity(controller);
		if (blockEntity instanceof BlueContainerBlockEntity)
			return (BlueContainerBlockEntity) blockEntity;
		return null;
	}

	public void removeController(boolean keepContents) {
		if (level.isClientSide())
			return;
		updateConnectivity = true;
		controller = null;
		radius = 1;
		length = 1;

		BlockState state = getBlockState();

			if (BlueContainerBlock.isContainer(state)) {
				state = state.setValue(BlueContainerBlock.LARGE, false);
				getLevel().setBlock(worldPosition, state, 22);
			}

		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public void setController(BlockPos controller) {
		if (level.isClientSide && !isVirtual())
			return;
		if (controller.equals(this.controller))
			return;
		this.controller = controller;
		itemCapability.invalidate();
		setChanged();
		sendData();
	}

	@Override
	public BlockPos getController() {
		return isController() ? worldPosition : controller;
	}

	@Override
	protected void read(CompoundTag compound, boolean clientPacket) {
		super.read(compound, clientPacket);

		BlockPos controllerBefore = controller;
		int prevSize = radius;
		int prevLength = length;

		updateConnectivity = compound.contains("Uninitialized");
		controller = null;
		lastKnownPos = null;

		if (compound.contains("LastKnownPos"))
			lastKnownPos = NbtUtils.readBlockPos(compound.getCompound("LastKnownPos"));
		if (compound.contains("Controller"))
			controller = NbtUtils.readBlockPos(compound.getCompound("Controller"));

		if (isController()) {
			radius = compound.getInt("Size");
			length = compound.getInt("Length");
		}

		if (!clientPacket) {
			inventory.deserializeNBT(compound.getCompound("Inventory"));
			return;
		}

		boolean changeOfController =
			controllerBefore == null ? controller != null : !controllerBefore.equals(controller);
		if (hasLevel() && (changeOfController || prevSize != radius || prevLength != length))
			level.setBlocksDirty(getBlockPos(), Blocks.AIR.defaultBlockState(), getBlockState());
	}

	@Override
	protected void write(CompoundTag compound, boolean clientPacket) {
		if (updateConnectivity)
			compound.putBoolean("Uninitialized", true);
		if (lastKnownPos != null)
			compound.put("LastKnownPos", NbtUtils.writeBlockPos(lastKnownPos));
		if (!isController())
			compound.put("Controller", NbtUtils.writeBlockPos(controller));
		if (isController()) {
			compound.putInt("Size", radius);
			compound.putInt("Length", length);
		}

		if (!clientPacket) {
			compound.putString("StorageType", "CombinedInv");
			compound.put("Inventory", inventory.serializeNBT());
		}
	}

	public ItemStackHandler getInventoryOfBlock() {
		return inventory;
	}

	public void applyInventoryToBlock(ItemStackHandler handler) {
		for (int i = 0; i < inventory.getSlots(); i++)
			inventory.setStackInSlot(i, i < handler.getSlots() ? handler.getStackInSlot(i) : ItemStack.EMPTY);
	}

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
		if (isItemHandlerCap(cap)) {
			initCapability();
			return itemCapability.cast();
		}
		return super.getCapability(cap, side);
	}

	private void initCapability() {
		if (itemCapability.isPresent())
			return;
		if (!isController()) {
			BlueContainerBlockEntity controllerBE = getControllerBE();
			if (controllerBE == null)
				return;
			controllerBE.initCapability();
			itemCapability = controllerBE.itemCapability;
			return;
		}

		boolean	alongZ = BlueContainerBlock.getContainerBlockAxis(getBlockState()) == Axis.Z;

		IItemHandlerModifiable[] invs = new IItemHandlerModifiable[length * radius * radius];
		for (int yOffset = 0; yOffset < length; yOffset++) {
			for (int xOffset = 0; xOffset < radius; xOffset++) {
				for (int zOffset = 0; zOffset < radius; zOffset++) {
					BlockPos containerPos = alongZ ? worldPosition.offset(xOffset, zOffset, yOffset)
						: worldPosition.offset(yOffset, xOffset, zOffset);
					BlueContainerBlockEntity containerAt =
						ConnectivityHandler.partAt(MmbBlockEntities.BLUE_CONTAINER.get(), level, containerPos);
					invs[yOffset * radius * radius + xOffset * radius + zOffset] =
						containerAt != null ? containerAt.inventory : new ItemStackHandler();
				}
			}
		}

		CombinedInvWrapper combinedInvWrapper = new CombinedInvWrapper(invs);
		itemCapability = LazyOptional.of(() -> combinedInvWrapper);
	}

	public static int getMaxLength(int radius) {
		return radius * 3;
	}

	@Override
	public void preventConnectivityUpdate() { updateConnectivity = false; }

	@Override
	public void notifyMultiUpdated() {
		BlockState state = this.getBlockState();

			if (BlueContainerBlock.isContainer(state)) { // safety
				level.setBlock(getBlockPos(), state.setValue(BlueContainerBlock.LARGE, radius > 2), 6);
			}

		itemCapability.invalidate();
		setChanged();
	}

	@Override
	public Axis getMainConnectionAxis() { return getMainAxisOf(this); }

	@Override
	public int getMaxLength(Axis longAxis, int width) {
		if (longAxis == Axis.Y) return getMaxWidth();
		return getMaxLength(width);
	}

	@Override
	public int getMaxWidth() {
		return 3;
	}

	@Override
	public int getHeight() { return length; }

	@Override
	public int getWidth() { return radius; }

	@Override
	public void setHeight(int height) { this.length = height; }

	@Override
	public void setWidth(int width) { this.radius = width; }

	@Override
	public boolean hasInventory() { return true; }
}

src/main/java/com/mangomilk/design_decor/blocks/industrial_gear/IndustrialGearRenderer.java
public class IndustrialGearRenderer extends KineticBlockEntityRenderer<BracketedKineticBlockEntity> {
    public IndustrialGearRenderer(BlockEntityRendererProvider.Context context) {
        super(context);
    }

    @Override
    protected void renderSafe(BracketedKineticBlockEntity be, float partialTicks, PoseStack ms,
                              MultiBufferSource buffer, int light, int overlay) {

        if (Backend.canUseInstancing(be.getLevel()))
            return;

        if (!MmbBlocks.LARGE_COGWHEEL.has(be.getBlockState())) {
            super.renderSafe(be, partialTicks, ms, buffer, light, overlay);
            return;
        }

        // Large cogs sometimes have to offset their teeth by 11.25 degrees in order to
        // mesh properly

        Direction.Axis axis = getRotationAxisOf(be);
        Direction facing = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
        renderRotatingBuffer(be,
                CachedBufferer.partialFacingVertical(DecoPartialModels.SHAFTLESS_LARGE_COGWHEEL, be.getBlockState(), facing),
                ms, buffer.getBuffer(RenderType.solid()), light);

        float angle = getAngleForLargeCogShaft(be, axis);
        SuperByteBuffer shaft =
                CachedBufferer.partialFacingVertical(DecoPartialModels.EMPTY, be.getBlockState(), facing);
        kineticRotationTransform(shaft, be, axis, angle, light);
        shaft.renderInto(ms, buffer.getBuffer(RenderType.solid()));

    }

    public static float getAngleForLargeCogShaft(SimpleKineticBlockEntity be, Direction.Axis axis) {
        BlockPos pos = be.getBlockPos();
        float offset = getShaftAngleOffset(axis, pos);
        float time = AnimationTickHolder.getRenderTime(be.getLevel());
        float angle = ((time * be.getSpeed() * 3f / 10 + offset) % 360) / 180 * (float) Math.PI;
        return angle;
    }

    public static float getShaftAngleOffset(Direction.Axis axis, BlockPos pos) {
        float offset = 0;
        double d = (((axis == Direction.Axis.X) ? 0 : pos.getX()) + ((axis == Direction.Axis.Y) ? 0 : pos.getY())
                + ((axis == Direction.Axis.Z) ? 0 : pos.getZ())) % 2;
        if (d == 0)
            offset = 22.5f;
        return offset;
    }
}

src/main/java/com/mangomilk/design_decor/blocks/gas_tank/GasTankBlockEntity.java
public class GasTankBlockEntity extends SmartBlockEntity implements IHaveGoggleInformation {

    protected FluidTank tankInventory;

    protected LazyOptional<IFluidHandler> fluidCapability;

    public GasTankBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
        super(type, pos, state);

        tankInventory = new SmartFluidTank(5000,this::onFluidStackChanged);

        fluidCapability = LazyOptional.of(() -> tankInventory);
    }

    @Nonnull
    @Override
    @SuppressWarnings("removal")
    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, Direction side) {

        if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)
            return fluidCapability.cast();
        return super.getCapability(cap, side);
    }
    @Override
    public void invalidate() {
        super.invalidate();

        fluidCapability.invalidate();
    }

    @Override
    protected void read(CompoundTag compound, boolean clientPacket) {
        super.read(compound, clientPacket);

        tankInventory.readFromNBT(compound.getCompound("TankContent"));

    }
    protected void onFluidStackChanged(FluidStack newFluidStack) {
        if (!hasLevel())
            return;

        if (!level.isClientSide) {
            setChanged();
            sendData();
        }

    }
    @Override
    public void write(CompoundTag compound, boolean clientPacket) {
        super.write(compound, clientPacket);

        compound.put("TankContent", tankInventory.writeToNBT(new CompoundTag()));

    }
    @Override
    public void addBehaviours(List<BlockEntityBehaviour> behaviours) {}
    @Override
    @SuppressWarnings("removal")
    public boolean addToGoggleTooltip(List<Component> tooltip, boolean isPlayerSneaking) {

        //--Fluid Info--//
        LazyOptional<IFluidHandler> handler = this.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY);
        Optional<IFluidHandler> resolve = handler.resolve();
        if (!resolve.isPresent())
            return false;

        IFluidHandler tank = resolve.get();
        if (tank.getTanks() == 0)
            return false;

        LangBuilder mb = Lang.translate("generic.unit.millibuckets");

        boolean isEmpty = true;
        for (int i = 0; i < tank.getTanks(); i++) {
            FluidStack fluidStack = tank.getFluidInTank(i);
            if (fluidStack.isEmpty())
                continue;

            Lang.fluidName(fluidStack)
                    .style(ChatFormatting.GRAY)
                    .forGoggles(tooltip, 1);

            Lang.builder()
                    .add(Lang.number(fluidStack.getAmount())
                            .add(mb)
                            .style(ChatFormatting.DARK_PURPLE))
                    .text(ChatFormatting.GRAY, " / ")
                    .add(Lang.number(tank.getTankCapacity(i))
                            .add(mb)
                            .style(ChatFormatting.DARK_GRAY))
                    .forGoggles(tooltip, 1);

            isEmpty = false;
        }

        if (tank.getTanks() > 1) {
            if (isEmpty)
                tooltip.remove(tooltip.size() - 1);
            return true;
        }

        if (!isEmpty)
            return true;

        Lang.translate("gui.goggles.fluid_container.capacity")
                .add(Lang.number(tank.getTankCapacity(0))
                        .add(mb)
                        .style(ChatFormatting.DARK_PURPLE))
                .style(ChatFormatting.DARK_GRAY)
                .forGoggles(tooltip, 1);

        return true;
    }
}

src/main/java/com/mangomilk/design_decor/blocks/industrial_gear/IndustrialGearInstance.java
public class IndustrialGearInstance  extends SingleRotatingInstance<BracketedKineticBlockEntity> {

    protected RotatingData additionalShaft;

    public IndustrialGearInstance(MaterialManager materialManager, BracketedKineticBlockEntity blockEntity) {
        super(materialManager, blockEntity);
    }

    @Override
    public void init() {
        super.init();
        if (!ICogWheel.isLargeCog(blockEntity.getBlockState()))
            return;

        // Large cogs sometimes have to offset their teeth by 11.25 degrees in order to
        // mesh properly

        float speed = blockEntity.getSpeed();
        Direction.Axis axis = KineticBlockEntityRenderer.getRotationAxisOf(blockEntity);
        BlockPos pos = blockEntity.getBlockPos();
        float offset = BracketedKineticBlockEntityRenderer.getShaftAngleOffset(axis, pos);
        Direction facing = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
        Instancer<RotatingData> half = getRotatingMaterial().getModel(DecoPartialModels.EMPTY, blockState,
                facing, () -> this.rotateToAxis(axis));

        additionalShaft = setup(half.createInstance(), speed);
        additionalShaft.setRotationOffset(offset);
    }

    @Override
    protected Instancer<RotatingData> getModel() {
        if (!ICogWheel.isLargeCog(blockEntity.getBlockState()))
            return super.getModel();

        Direction.Axis axis = KineticBlockEntityRenderer.getRotationAxisOf(blockEntity);
        Direction facing = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
        return getRotatingMaterial().getModel(DecoPartialModels.SHAFTLESS_LARGE_COGWHEEL, blockState, facing,
                () -> this.rotateToAxis(axis));
    }

    private PoseStack rotateToAxis(Direction.Axis axis) {
        Direction facing = Direction.fromAxisAndDirection(axis, Direction.AxisDirection.POSITIVE);
        PoseStack poseStack = new PoseStack();
        TransformStack.cast(poseStack)
                .centre()
                .rotateToFace(facing)
                .multiply(Vector3f.XN.rotationDegrees(-90))
                .unCentre();
        return poseStack;
    }

    @Override
    public void update() {
        super.update();
        if (additionalShaft != null) {
            updateRotation(additionalShaft);
            additionalShaft.setRotationOffset(BracketedKineticBlockEntityRenderer.getShaftAngleOffset(axis, pos));
        }
    }

    @Override
    public void updateLight() {
        super.updateLight();
        if (additionalShaft != null)
            relight(pos, additionalShaft);
    }

    @Override
    public void remove() {
        super.remove();
        if (additionalShaft != null)
            additionalShaft.delete();
    }

}

src/main/java/com/mangomilk/design_decor/blocks/crushing_wheels/MmbCrushingWheelControllerBlockEntity.java
public class MmbCrushingWheelControllerBlockEntity extends CrushingWheelControllerBlockEntity {
	private UUID entityUUID;
	private RecipeWrapper wrapper;
	public MmbCrushingWheelControllerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
		super(type, pos, state);
		wrapper = new RecipeWrapper(inventory);
	}

	@Override
	public void tick() {
		super.tick();
		if (searchForEntity) {
			searchForEntity = false;
			List<Entity> search = level.getEntities((Entity) null, new AABB(getBlockPos()),
				e -> entityUUID.equals(e.getUUID()));
			if (search.isEmpty())
				clear();
			else
				processingEntity = search.get(0);
		}

		if (!isOccupied())
			return;
		if (crushingspeed == 0)
			return;

		if (level.isClientSide)
			DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.tickAudio());

		float speed = crushingspeed * 4;

		Vec3 centerPos = VecHelper.getCenterOf(worldPosition);
		Direction facing = getBlockState().getValue(MmbCrushingWheelControllerBlock.FACING);
		int offset = facing.getAxisDirection()
			.getStep();
		Vec3 outSpeed = new Vec3((facing.getAxis() == Axis.X ? 0.25D : 0.0D) * offset,
			offset == 1 ? (facing.getAxis() == Axis.Y ? 0.5D : 0.0D) : 0.0D // Increased upwards speed so upwards
																			// crushing wheels shoot out the item
																			// properly.
			, (facing.getAxis() == Axis.Z ? 0.25D : 0.0D) * offset); // No downwards speed, so downwards crushing wheels
																		// drop the items as before.
		Vec3 outPos = centerPos.add((facing.getAxis() == Axis.X ? .55f * offset : 0f),
			(facing.getAxis() == Axis.Y ? .55f * offset : 0f), (facing.getAxis() == Axis.Z ? .55f * offset : 0f));

		if (!hasEntity()) {

			float processingSpeed =
				Mth.clamp((speed) / (!inventory.appliedRecipe ? Mth.log2(inventory.getStackInSlot(0)
					.getCount()) : 1), .25f, 20);
			inventory.remainingTime -= processingSpeed;
			spawnParticles(inventory.getStackInSlot(0));

			if (level.isClientSide)
				return;

			if (inventory.remainingTime < 20 && !inventory.appliedRecipe) {
				applyRecipe();
				inventory.appliedRecipe = true;
				level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 2 | 16);
				return;
			}

			if (inventory.remainingTime > 0) {
				return;
			}
			inventory.remainingTime = 0;

			// Output Items
			if (facing != Direction.UP) {
				BlockPos nextPos = worldPosition.offset(facing.getAxis() == Axis.X ? 1f * offset : 0f, (-1f),
					facing.getAxis() == Axis.Z ? 1f * offset : 0f);
				DirectBeltInputBehaviour behaviour =
					BlockEntityBehaviour.get(level, nextPos, DirectBeltInputBehaviour.TYPE);
				if (behaviour != null) {
					boolean changed = false;
					if (!behaviour.canInsertFromSide(facing))
						return;
					for (int slot = 0; slot < inventory.getSlots(); slot++) {
						ItemStack stack = inventory.getStackInSlot(slot);
						if (stack.isEmpty())
							continue;
						ItemStack remainder = behaviour.handleInsertion(stack, facing, false);
						if (remainder.equals(stack, false))
							continue;
						inventory.setStackInSlot(slot, remainder);
						changed = true;
					}
					if (changed) {
						setChanged();
						sendData();
					}
					return;
				}
			}

			// Eject Items
			for (int slot = 0; slot < inventory.getSlots(); slot++) {
				ItemStack stack = inventory.getStackInSlot(slot);
				if (stack.isEmpty())
					continue;
				ItemEntity entityIn = new ItemEntity(level, outPos.x, outPos.y, outPos.z, stack);
				entityIn.setDeltaMovement(outSpeed);
				entityIn.getPersistentData()
					.put("BypassCrushingWheel", NbtUtils.writeBlockPos(worldPosition));
				level.addFreshEntity(entityIn);
			}
			inventory.clear();
			level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 2 | 16);

			return;
		}

		if (!processingEntity.isAlive() || !processingEntity.getBoundingBox()
			.intersects(new AABB(worldPosition).inflate(.5f))) {
			clear();
			return;
		}

		double xMotion = ((worldPosition.getX() + .5f) - processingEntity.getX()) / 2f;
		double zMotion = ((worldPosition.getZ() + .5f) - processingEntity.getZ()) / 2f;
		if (processingEntity.isShiftKeyDown())
			xMotion = zMotion = 0;
		double movement = Math.max(-speed / 4f, -.5f) * -offset;
		processingEntity.setDeltaMovement(
			new Vec3(facing.getAxis() == Axis.X ? movement : xMotion, facing.getAxis() == Axis.Y ? movement : 0f // Do
																														// not
																														// move
																														// entities
																														// upwards
																														// or
																														// downwards
																														// for
																														// horizontal
																														// crushers,
				, facing.getAxis() == Axis.Z ? movement : zMotion)); // Or they'll only get their feet crushed.

		if (level.isClientSide)
			return;

		if (!(processingEntity instanceof ItemEntity)) {
			Vec3 entityOutPos = outPos.add(facing.getAxis() == Axis.X ? .5f * offset : 0f,
				facing.getAxis() == Axis.Y ? .5f * offset : 0f, facing.getAxis() == Axis.Z ? .5f * offset : 0f);
			int crusherDamage = AllConfigs.server().kinetics.crushingDamage.get();

			if (processingEntity instanceof LivingEntity) {
				if ((((LivingEntity) processingEntity).getHealth() - crusherDamage <= 0) // Takes LivingEntity instances
																							// as exception, so it can
																							// move them before it would
																							// kill them.
					&& (((LivingEntity) processingEntity).hurtTime <= 0)) { // This way it can actually output the items
																			// to the right spot.
					processingEntity.setPos(entityOutPos.x, entityOutPos.y, entityOutPos.z);
				}
			}
			processingEntity.hurt(CrushingWheelBlockEntity.DAMAGE_SOURCE, crusherDamage);
			if (!processingEntity.isAlive()) {
				processingEntity.setPos(entityOutPos.x, entityOutPos.y, entityOutPos.z);
			}
			return;
		}

		ItemEntity itemEntity = (ItemEntity) processingEntity;
		itemEntity.setPickUpDelay(20);
		if (facing.getAxis() == Axis.Y) {
			if (processingEntity.getY() * -offset < (centerPos.y - .25f) * -offset) {
				intakeItem(itemEntity);
			}
		} else if (facing.getAxis() == Axis.Z) {
			if (processingEntity.getZ() * -offset < (centerPos.z - .25f) * -offset) {
				intakeItem(itemEntity);
			}
		} else {
			if (processingEntity.getX() * -offset < (centerPos.x - .25f) * -offset) {
				intakeItem(itemEntity);
			}
		}
	}

	@OnlyIn(Dist.CLIENT)
	public void tickAudio() {
		float pitch = Mth.clamp((crushingspeed / 256f) + .45f, .85f, 1f);
		if (entityUUID == null && inventory.getStackInSlot(0)
			.isEmpty())
			return;
		SoundScapes.play(AmbienceGroup.CRUSHING, worldPosition, pitch);
	}

	private void intakeItem(ItemEntity itemEntity) {
		inventory.clear();
		inventory.setStackInSlot(0, itemEntity.getItem()
			.copy());
		itemInserted(inventory.getStackInSlot(0));
		itemEntity.discard();
		level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 2 | 16);
	}

	protected void spawnParticles(ItemStack stack) {
		if (stack == null || stack.isEmpty())
			return;

		ParticleOptions particleData = null;
		if (stack.getItem() instanceof BlockItem)
			particleData = new BlockParticleOption(ParticleTypes.BLOCK, ((BlockItem) stack.getItem()).getBlock()
				.defaultBlockState());
		else
			particleData = new ItemParticleOption(ParticleTypes.ITEM, stack);

		RandomSource r = level.random;
		for (int i = 0; i < 4; i++)
			level.addParticle(particleData, worldPosition.getX() + r.nextFloat(), worldPosition.getY() + r.nextFloat(),
				worldPosition.getZ() + r.nextFloat(), 0, 0, 0);
	}

	private void applyRecipe() {
		Optional<ProcessingRecipe<RecipeWrapper>> recipe = findRecipe();

		List<ItemStack> list = new ArrayList<>();
		if (recipe.isPresent()) {
			int rolls = inventory.getStackInSlot(0)
				.getCount();
			inventory.clear();
			for (int roll = 0; roll < rolls; roll++) {
				List<ItemStack> rolledResults = recipe.get()
					.rollResults();
				for (int i = 0; i < rolledResults.size(); i++) {
					ItemStack stack = rolledResults.get(i);
					ItemHelper.addToList(stack, list);
				}
			}
			for (int slot = 0; slot < list.size() && slot + 1 < inventory.getSlots(); slot++)
				inventory.setStackInSlot(slot + 1, list.get(slot));
		} else {
			inventory.clear();
		}

	}

	public Optional<ProcessingRecipe<RecipeWrapper>> findRecipe() {
		Optional<ProcessingRecipe<RecipeWrapper>> crushingRecipe = AllRecipeTypes.CRUSHING.find(wrapper, level);
		if (!crushingRecipe.isPresent())
			crushingRecipe = AllRecipeTypes.MILLING.find(wrapper, level);
		return crushingRecipe;
	}

	@Override
	public void write(CompoundTag compound, boolean clientPacket) {
		if (hasEntity())
			compound.put("Entity", NbtUtils.createUUID(entityUUID));
		compound.put("Inventory", inventory.serializeNBT());
		compound.putFloat("Speed", crushingspeed);
	}

	@Override
	protected void read(CompoundTag compound, boolean clientPacket) {
		super.read(compound, clientPacket);
		if (compound.contains("Entity") && !isOccupied()) {
			entityUUID = NbtUtils.loadUUID(NBTHelper.getINBT(compound, "Entity"));
			this.searchForEntity = true;
		}
		crushingspeed = compound.getFloat("Speed");
		inventory.deserializeNBT(compound.getCompound("Inventory"));
	}

	public void startCrushing(Entity entity) {
		processingEntity = entity;
		entityUUID = entity.getUUID();
	}

	private void itemInserted(ItemStack stack) {
		Optional<ProcessingRecipe<RecipeWrapper>> recipe = findRecipe();
		inventory.remainingTime = recipe.isPresent() ? recipe.get()
			.getProcessingDuration() : 100;
		inventory.appliedRecipe = false;
	}

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
		if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
			return handler.cast();
		return super.getCapability(cap, side);
	}

	public void clear() {
		processingEntity = null;
		entityUUID = null;
	}

	public boolean isOccupied() {
		return hasEntity() || !inventory.isEmpty();
	}

	public boolean hasEntity() {
		return processingEntity != null;
	}

}

Answer: .blockEntity("mmb_crushing_wheel_controller", MmbCrushingWheelControllerBlockEntity::new)

<think>
The code defines a static BlockEntityEntry for the crushing wheel controller. Following the pattern used for other entries, after the assignment line the builder chain continues with a call to .blockEntity providing the registry name and constructor reference. Therefore the next line should be the .blockEntity call with the appropriate name and class.
</think>
.blockEntity("mmb_crushing_wheel_controller", MmbCrushingWheelControllerBlockEntity::new)