001package org.javasimon.console.action;
002
003import org.javasimon.Sample;
004import org.javasimon.Simon;
005import org.javasimon.console.Action;
006import org.javasimon.console.ActionContext;
007import org.javasimon.console.ActionException;
008import org.javasimon.console.SimonType;
009import org.javasimon.console.SimonTypeFactory;
010import org.javasimon.console.SimonVisitor;
011import org.javasimon.console.SimonVisitors;
012import org.javasimon.console.TimeFormatType;
013import org.javasimon.console.reflect.Getter;
014import org.javasimon.console.reflect.GetterFactory;
015import org.javasimon.console.text.Stringifier;
016import org.javasimon.console.text.StringifierFactory;
017
018import java.io.IOException;
019import java.io.PrintWriter;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Set;
023
024import javax.servlet.ServletException;
025
026/**
027 * Base class for exporting Simons as tabular data.
028 * The following methods are called during rendering and can be overridden:
029 * <ul>
030 * <li>{@link #printTable }: <ul>
031 * <li>{@link #printHeaderRow}: <ul>
032 * <li>{@link #printHeaderCell} : {@link #printCell} </li>
033 * </ul></li>
034 * <li>{@link #printBody}: <ul>
035 * <li>{@link #printBodyRow}: <ul>
036 * <li>{@link #printBodyCell} : {@link #printCell} </li>
037 * </ul></li>
038 * </ul></li>
039 * </ul></li>
040 * </ul>
041 *
042 * @author gquintana
043 */
044public class AbstractTableAction extends Action {
045
046        /** Value formatter. */
047        protected StringifierFactory stringifierFactory;
048        /** Pattern for Simon name filtering. */
049        private String pattern;
050        /** Types for Simon type filtering. */
051        private Set<SimonType> types;
052        /** Column list in response. */
053        private List<Column> columns = new ArrayList<>();
054        /** Content type of response. */
055        protected final String contentType;
056        /** Decimal format pattern used for printing doubles. */
057        protected String numberPattern = StringifierFactory.READABLE_NUMBER_PATTERN;
058
059        /** Base constructor initializes columns list. */
060        protected AbstractTableAction(ActionContext context, String contentType) {
061                super(context);
062                this.contentType = contentType;
063                columns.add(new Column<String>("Name", "name"));
064                columns.add(new Column<SimonType>("Type", "type") {
065                        private Stringifier<SimonType> stringifier;
066
067                        @Override
068                        public SimonType getValue(Object object) {
069                                return SimonTypeFactory.getValueFromInstance((Sample) object);
070                        }
071
072                        @Override
073                        public String getFormattedValue(Object object) {
074                                return getStringifier(object).toString(getValue(object));
075                        }
076
077                        @Override
078                        public Stringifier<SimonType> getStringifier(Object object) {
079                                if (stringifier == null) {
080                                        stringifier = stringifierFactory.getStringifier(SimonType.class);
081                                }
082                                return stringifier;
083                        }
084                });
085                columns.add(new Column<Long>("Counter", "counter"));
086                columns.add(new Column<Long>("Total", "total"));
087                columns.add(new Column<Long>("Min", "min"));
088                columns.add(new Column<Long>("Mean", "mean"));
089                columns.add(new Column<Long>("Last", "last"));
090                columns.add(new Column<Long>("Max", "max"));
091                columns.add(new Column<Double>("Std Dev", "standardDeviation"));
092                columns.add(new Column<Long>("First Use", "firstUsage"));
093                columns.add(new Column<Long>("Last Use", "lastUsage"));
094                columns.add(new Column<String>("Note", "note"));
095
096        }
097
098        public String getPattern() {
099                return pattern;
100        }
101
102        public void setPattern(String pattern) {
103                this.pattern = pattern;
104        }
105
106        @Override
107        public void readParameters() {
108                TimeFormatType timeFormat = getContext().getParameterAsEnum("timeFormat", TimeFormatType.class, TimeFormatType.MILLISECOND);
109                stringifierFactory.init(timeFormat,
110                        StringifierFactory.READABLE_DATE_PATTERN,
111                        numberPattern);
112                pattern = getContext().getParameterAsString("pattern", null);
113                types = getContext().getParametersAsEnums("type", SimonType.class, null);
114        }
115
116        protected void printTable(PrintWriter writer) throws IOException {
117                printHeaderRow(writer);
118                printBody(writer);
119        }
120
121        protected void printHeaderRow(PrintWriter writer) throws IOException {
122                for (Column column : columns) {
123                        printHeaderCell(column, writer);
124                }
125        }
126
127        protected void printHeaderCell(Column c, PrintWriter writer) throws IOException {
128                printCell(c, c.getTitle(), writer);
129        }
130
131        protected void printBody(PrintWriter writer) throws IOException {
132                SimonVisitors.visitList(getContext().getManager(), pattern, types, new SimonVisitorImpl(writer));
133        }
134
135        protected void printBodyRow(Simon simon, PrintWriter writer) throws IOException {
136                printBodyRow(simon.sample(), writer);
137        }
138
139        protected void printBodyRow(Sample sample, PrintWriter writer) throws IOException {
140                for (Column column : columns) {
141                        printBodyCell(column, sample, writer);
142                }
143        }
144
145        protected void printBodyCell(Column c, Sample sample, PrintWriter writer) throws IOException {
146                printCell(c, c.getFormattedValue(sample), writer);
147        }
148
149        protected void printCell(Column c, String s, PrintWriter writer) throws IOException {
150                if (s != null) {
151                        writer.write(s);
152                }
153        }
154
155        public void execute() throws ServletException, IOException, ActionException {
156                dontCache();
157                PrintWriter writer = null;
158                try {
159                        getContext().setContentType(contentType);
160                        writer = getContext().getWriter();
161                        printTable(writer);
162                } finally {
163                        if (writer != null) {
164                                writer.flush();
165                        }
166                }
167        }
168
169        private class SimonVisitorImpl implements SimonVisitor {
170
171                private final PrintWriter writer;
172
173                public SimonVisitorImpl(PrintWriter writer) {
174                        this.writer = writer;
175                }
176
177                public void visit(Simon simon) throws IOException {
178                        printBodyRow(simon, writer);
179                }
180        }
181
182        protected class Column<T> {
183                /** Column title/header. */
184                private final String title;
185                /** Column property name. */
186                private final String name;
187
188                public Column(String title, String name) {
189                        this.title = title;
190                        this.name = name;
191                }
192
193                /** Get column property name. */
194                public String getName() {
195                        return name;
196                }
197
198                /** Get column property title/header. */
199                public String getTitle() {
200                        return title;
201                }
202
203                /**
204                 * Returns getter for given column and row (simon).
205                 *
206                 * @param object Simon row
207                 * @return getter
208                 */
209                @SuppressWarnings("unchecked")
210                private Getter<T> getGetter(Object object) {
211                        return GetterFactory.getGetter(object.getClass(), name);
212                }
213
214                /** Returns raw column value. */
215                public T getValue(Object object) {
216                        Getter<T> getter = getGetter(object);
217                        return getter == null ? null : getter.get(object);
218                }
219
220                /**
221                 * Get stringier used for given column and row.
222                 *
223                 * @param object Row (simon)
224                 * @return Stringifier
225                 */
226                public Stringifier<T> getStringifier(Object object) {
227                        Getter<T> getter = getGetter(object);
228                        if (getter == null) {
229                                return stringifierFactory.getNullStringifier();
230                        } else {
231                                return stringifierFactory.getStringifier(getter.getType(), getter.getSubType());
232                        }
233                }
234
235                /** Get formatted column value. */
236                public String getFormattedValue(Object object) {
237                        Getter<T> getter = getGetter(object);
238                        if (getter == null) {
239                                return stringifierFactory.toString(null);
240                        } else {
241                                return stringifierFactory.toString(getter.get(object), getter.getSubType());
242                        }
243                }
244        }
245}