/* * Copyright (c) 2012-2015 iWave Software LLC * All Rights Reserved */ package com.emc.aix.command.parse; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import org.apache.commons.beanutils.ConvertUtilsBean; import com.google.common.collect.Lists; /** * @author logelj * * <p> * The TextOutputUnmarshaller is a parsing utility for used with textual, tabular data input, often found with command line output. * It reflectively analyses classes annotated with @TextObject and splits, parses, and converts input text into target domain Java * objects. * </p> * * Text Input: * * <p> * <blockquote> * * <pre> * My Command Line Output Version 1.2.3.4 * EMC Corporation * * name age employee number dob * =================================================================== * Jay 37 12345 Apr 20 1977 * Mel 14 54311 Sep 07 2000 * Maddy 11 32478 Jun 05 2003 * Jojo 9 48399 Jul 02 2005 * </pre> * * </blockquote> * </p> * * Domain object: * * <pre> * @TextObject(startLine = 6) * class Employee { * * @Position(1) * private String name; * * @Position(2) * private int age; * * @Position(3) * private double employeeId; * * @MultiPosition(value = { 4, 5, 6 }, formatter = MyMultiFieldFormatter.class) * private Date dob; * } * </pre> * * Call the unmarshaller as follows: * <p> * <blockquote> * * <pre> * TextOuputUnmarshaller unmarshaller = TextOuputUnmarshaller.instance(); * List<Employee> employees = unmarshaller.with(inputText).parse(Employee.class); * * </pre> * * </blockquote> * </p> */ public final class TextOutputUnmarshaller { private static final String DEFAULT_LINE_SPLITTER = "\\s+"; private static final int UNDEFINED_EOL = -1; private ConvertUtilsBean converter = new ConvertUtilsBean(); private String text; private static TextOutputUnmarshaller instance; static { instance = new TextOutputUnmarshaller(); } private TextOutputUnmarshaller() { } public static TextOutputUnmarshaller instance() { return instance; } public TextOutputUnmarshaller with(String text) { this.text = text; return this; } public <T> List<T> parse(Class<? extends T> clazz) throws ParseException { List<T> results; try { int startLine = 0; int endLine = UNDEFINED_EOL; String lineSeparator = DEFAULT_LINE_SPLITTER; TextObject to = clazz.getAnnotation(TextObject.class); if (to != null) { startLine = to.startLine(); endLine = to.endLine(); lineSeparator = to.separatorExpression(); } results = Lists.newArrayList(); if (text != null) { String[] textLines = text.split(System.getProperty("line.separator")); List<String> linesList = Lists.newArrayList(textLines); List<String> lines; if (endLine != UNDEFINED_EOL) { lines = linesList.subList(startLine - 1, endLine); } else { lines = linesList.subList(startLine - 1, linesList.size()); } for (String line : lines) { String[] attributes = line.split(lineSeparator); T object = clazz.newInstance(); int fieldPosition = 0; for (Field f : clazz.getDeclaredFields()) { f.setAccessible(true); Object value = null; if (f.isAnnotationPresent(Position.class)) { Position position = f.getAnnotation(Position.class); value = processPosition(position, attributes, fieldPosition, f); } else if (f.isAnnotationPresent(MultiPosition.class)) { MultiPosition multi = f.getAnnotation(MultiPosition.class); value = processMultiPosition(multi, attributes, fieldPosition, f); } f.set(object, value); fieldPosition++; } results.add(object); } } } catch (InstantiationException | IllegalAccessException | SecurityException | IllegalArgumentException e) { throw new ParseException(e); } return results; } private Object processPosition(Position position, String[] attributes, int fieldPosition, Field f) throws InstantiationException, IllegalAccessException { Object value = null; if (position != null) { FieldFormatter formatter = position.formatter().newInstance(); value = converter.convert(formatter.format(attributes[position.value() - 1]), f.getType()); } else { // assume field position as the default value = converter.convert(attributes[fieldPosition], f.getType()); } return value; } private Object processMultiPosition(MultiPosition position, String[] attributes, int fieldPosition, Field f) throws InstantiationException, IllegalAccessException { Object value = null; MultiFieldFormatter formatter = position.formatter().newInstance(); int[] positions = position.value(); List<Object> positionValues = new ArrayList<Object>(); for (int p : positions) { if (checkRange(p, attributes)) { positionValues.add(attributes[p - 1]); } else { return null; } } Object[] array = positionValues.toArray(new Object[0]); value = converter.convert(formatter.format(array), f.getType()); return value; } private boolean checkRange(int index, String[] attributes) { return (index >= 0) && (index < attributes.length); } }