The aim of this puzzle is to create a formatter for a new text data format. The first step is to understand what the different element types are, and the rules of formatting. There are two things which are not clear in the statement according to me :
There are certainly many ways to solve this puzzle, so my solution is only an exemple. I never studied parsers or lexers, so my approche is probably quite "naive", and lacks efficiency. But it gives a cool example of inheritance / polymorphism, very important concepts of OOP.
import java.util.*; import java.io.*; import java.math.*; /** This class represents an element, which can be read from CGX content * and printed as a well formatted String */ abstract class Element { /** modifies the representation of an element, indenting it by 'indentLevel' spaces */ public static String indent(String toIndent, int indentLevel) { // Creates the indentation block StringBuilder sb = new StringBuilder(); for (int i = 0; i < indentLevel; i++) sb.append(" "); String indentation = sb.toString(); // Adds it in front of each line return indentation+toIndent.replace("\n", "\n"+indentation); } /** returns an int describing the type of the element beginning by content[offset] * type 1 : block type 2 : primitive type type 3 : key value * type 0 : none of the 3, probably a tabulation or a space */ public static int type(String content, int offset) { if (isBlock(content, offset)) return 1; else if (isKeyValue(content, offset)) return 3; else if (isPrimitive(content, offset)) return 2; else return 0; } /** reads next element of content and return new offset * for each type, should work only if content[offset] is really * the beginning of the given type. */ abstract int read(String content, int offset); /** returns a representation of the element, following the formatting * rules given in the puzzle */ abstract String toFormattedCGX(); // private functions used to identify each type /** returns true if and only if next Element in content is a Block */ private static boolean isBlock(String content, int offset) { return content.charAt(offset) == '('; } /** returns true if next Element in content is a Primitive type * be careful, also returns true if it is a KeyValue, so check with isKeyValue * returns false if it is not a Primitive or KeyValue */ private static boolean isPrimitive(String content, int offset) { return Character.isDigit(content.charAt(offset)) || content.regionMatches(offset, "true", 0, 4) || content.regionMatches(offset, "false", 0, 5) || content.regionMatches(offset, "null", 0, 4) || content.charAt(offset) == '\''; } /** returns true if and only if next Element in content is a KeyValue */ private static boolean isKeyValue(String content, int offset) { if (content.charAt(offset) != '\'') return false; else { while (content.charAt(++offset) != '\''); ++offset; while (offset < content.length() && (content.charAt(offset) == ' ' || content.charAt(offset) == '\t')) ++offset; return (offset < content.length() && content.charAt(offset) == '='); } } } class Block extends Element { private ArrayList<Element> elements; private static final int INDENT_LEVEL = 4; public Block() { elements = new ArrayList<Element>(); } public int read(String content, int offset) { // skips opening parenthesis offset++; // read each item : while (content.charAt(offset) != ')') { // skips optionnal ' ', tabulations and ';' while (content.charAt(offset) == ' ' || content.charAt(offset) == '\t' || content.charAt(offset) == ';') offset++; // if there is at least one item left if (content.charAt(offset) != ')') { Element nextElement = null; switch (Element.type(content, offset)) { case 1 : nextElement = new Block(); break; case 2 : nextElement = new Primitive(); break; case 3 : nextElement = new KeyValue(); break; } offset = nextElement.read(content, offset); elements.add(nextElement); } } // skip closing parenthesis and return new offset return ++offset; } public String toFormattedCGX() { StringBuilder result = new StringBuilder(); result.append("(\n"); if (elements.size() > 0) { for (Element e : elements) { result.append(indent(e.toFormattedCGX(), INDENT_LEVEL)); result.append(";\n"); } // remove last ';' and add closing parenthesis result.deleteCharAt(result.length()-2); } result.append(")"); return result.toString(); } } class Primitive extends Element { /** string representing the primitive : * true, false, null, a number, a string (with quotes)*/ private String value; public Primitive() {} public int read(String content, int offset) { /* sets i so that content[offset+i] is the 1st char after the primitive using the fact that for a string it follows ', for a number it is the is 1st non digital char, and for others it is the first non lower case */ int i = 0; if (content.charAt(offset) == '\'') { ++i; while (offset+i < content.length() && content.charAt(offset+i) != '\'') ++i; ++i; } else if (Character.isDigit(content.charAt(offset))) while (offset+i < content.length() && Character.isDigit(content.charAt(offset+i))) i++; else while (offset+i < content.length() && Character.isLowerCase(content.charAt(offset+i))) i++; value = content.substring(offset, offset+i); return offset+i; } public String toFormattedCGX() { return value; } } class KeyValue extends Element { // key, stored as a Primitive string private Primitive key; // value, which can be a block or a Primitive private Element value; public KeyValue() { key = new Primitive(); } public int read(String content, int offset) { offset = key.read(content, offset); // skips '=', and optionnal spaces while (Element.type(content, offset) == 0) offset++; switch (Element.type(content, offset)) { case 1 : value = new Block(); break; case 2 : value = new Primitive(); break; } offset = value.read(content, offset); return offset; } public String toFormattedCGX() { StringBuilder result = new StringBuilder(); result.append(key.toFormattedCGX()); result.append("="); if (value instanceof Block) result.append("\n"); result.append(value.toFormattedCGX()); return result.toString(); } } class Solution { private static String content; private static int offset = 0; public static void main(String args[]) { Scanner in = new Scanner(System.in); // Reading CGX content int N = in.nextInt(); in.nextLine(); StringBuilder CGXlines = new StringBuilder(); for (int i = 0; i < N; i++) CGXlines.append(in.nextLine()); content = CGXlines.toString(); // For each element, reads it, formats it and prints it while (offset < content.length()) { Element nextElement = null; switch (Element.type(content, offset)) { case 1 : nextElement = new Block(); break; case 2 : nextElement = new Primitive(); break; case 3 : nextElement = new KeyValue(); break; } if (nextElement == null) offset++; else { offset = nextElement.read(content, offset); System.out.println(nextElement.toFormattedCGX()); } } } }