| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| package org.apache.poi.ss.format; |
| |
| import java.util.ArrayList; |
| import java.util.Formatter; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Locale; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * This class implements printing out an elapsed time format. |
| * |
| * @author Ken Arnold, Industrious Media LLC |
| */ |
| public class CellElapsedFormatter extends CellFormatter { |
| private final List<TimeSpec> specs; |
| private TimeSpec topmost; |
| private final String printfFmt; |
| |
| private static final Pattern PERCENTS = Pattern.compile("%"); |
| |
| private static final double HOUR__FACTOR = 1.0 / 24.0; |
| private static final double MIN__FACTOR = HOUR__FACTOR / 60.0; |
| private static final double SEC__FACTOR = MIN__FACTOR / 60.0; |
| |
| private static class TimeSpec { |
| final char type; |
| final int pos; |
| final int len; |
| final double factor; |
| double modBy; |
| |
| public TimeSpec(char type, int pos, int len, double factor) { |
| this.type = type; |
| this.pos = pos; |
| this.len = len; |
| this.factor = factor; |
| modBy = 0; |
| } |
| |
| public long valueFor(double elapsed) { |
| double val; |
| if (modBy == 0) |
| val = elapsed / factor; |
| else |
| val = elapsed / factor % modBy; |
| if (type == '0') |
| return Math.round(val); |
| else |
| return (long) val; |
| } |
| } |
| |
| private class ElapsedPartHandler implements CellFormatPart.PartHandler { |
| // This is the one class that's directly using printf, so it can't use |
| // the default handling for quoted strings and special characters. The |
| // only special character for this is '%', so we have to handle all the |
| // quoting in this method ourselves. |
| |
| public String handlePart(Matcher m, String part, CellFormatType type, |
| StringBuffer desc) { |
| |
| int pos = desc.length(); |
| char firstCh = part.charAt(0); |
| switch (firstCh) { |
| case '[': |
| if (part.length() < 3) |
| break; |
| if (topmost != null) |
| throw new IllegalArgumentException( |
| "Duplicate '[' times in format"); |
| part = part.toLowerCase(Locale.ROOT); |
| int specLen = part.length() - 2; |
| topmost = assignSpec(part.charAt(1), pos, specLen); |
| return part.substring(1, 1 + specLen); |
| |
| case 'h': |
| case 'm': |
| case 's': |
| case '0': |
| part = part.toLowerCase(Locale.ROOT); |
| assignSpec(part.charAt(0), pos, part.length()); |
| return part; |
| |
| case '\n': |
| return "%n"; |
| |
| case '\"': |
| part = part.substring(1, part.length() - 1); |
| break; |
| |
| case '\\': |
| part = part.substring(1); |
| break; |
| |
| case '*': |
| if (part.length() > 1) |
| part = CellFormatPart.expandChar(part); |
| break; |
| |
| // An escape we can let it handle because it can't have a '%' |
| case '_': |
| return null; |
| } |
| // Replace ever "%" with a "%%" so we can use printf |
| return PERCENTS.matcher(part).replaceAll("%%"); |
| } |
| } |
| |
| /** |
| * Creates a elapsed time formatter. |
| * |
| * @param pattern The pattern to parse. |
| */ |
| public CellElapsedFormatter(String pattern) { |
| super(pattern); |
| |
| specs = new ArrayList<TimeSpec>(); |
| |
| StringBuffer desc = CellFormatPart.parseFormat(pattern, |
| CellFormatType.ELAPSED, new ElapsedPartHandler()); |
| |
| ListIterator<TimeSpec> it = specs.listIterator(specs.size()); |
| while (it.hasPrevious()) { |
| TimeSpec spec = it.previous(); |
| desc.replace(spec.pos, spec.pos + spec.len, "%0" + spec.len + "d"); |
| if (spec.type != topmost.type) { |
| spec.modBy = modFor(spec.type, spec.len); |
| } |
| } |
| |
| printfFmt = desc.toString(); |
| } |
| |
| private TimeSpec assignSpec(char type, int pos, int len) { |
| TimeSpec spec = new TimeSpec(type, pos, len, factorFor(type, len)); |
| specs.add(spec); |
| return spec; |
| } |
| |
| private static double factorFor(char type, int len) { |
| switch (type) { |
| case 'h': |
| return HOUR__FACTOR; |
| case 'm': |
| return MIN__FACTOR; |
| case 's': |
| return SEC__FACTOR; |
| case '0': |
| return SEC__FACTOR / Math.pow(10, len); |
| default: |
| throw new IllegalArgumentException( |
| "Uknown elapsed time spec: " + type); |
| } |
| } |
| |
| private static double modFor(char type, int len) { |
| switch (type) { |
| case 'h': |
| return 24; |
| case 'm': |
| return 60; |
| case 's': |
| return 60; |
| case '0': |
| return Math.pow(10, len); |
| default: |
| throw new IllegalArgumentException( |
| "Uknown elapsed time spec: " + type); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void formatValue(StringBuffer toAppendTo, Object value) { |
| double elapsed = ((Number) value).doubleValue(); |
| |
| if (elapsed < 0) { |
| toAppendTo.append('-'); |
| elapsed = -elapsed; |
| } |
| |
| Object[] parts = new Long[specs.size()]; |
| for (int i = 0; i < specs.size(); i++) { |
| parts[i] = specs.get(i).valueFor(elapsed); |
| } |
| |
| Formatter formatter = new Formatter(toAppendTo, Locale.ROOT); |
| try { |
| formatter.format(printfFmt, parts); |
| } finally { |
| formatter.close(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p/> |
| * For a date, this is <tt>"mm/d/y"</tt>. |
| */ |
| public void simpleValue(StringBuffer toAppendTo, Object value) { |
| formatValue(toAppendTo, value); |
| } |
| } |