package com.bao.lc.site.s2.commands; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.chain.Context; import org.apache.commons.chain.impl.ContextBase; import org.apache.commons.collections.MapUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.htmlparser.Node; import org.htmlparser.NodeFilter; import org.htmlparser.Parser; import org.htmlparser.Tag; import org.htmlparser.filters.AndFilter; import org.htmlparser.filters.HasAttributeFilter; import org.htmlparser.filters.NodeClassFilter; import org.htmlparser.filters.OrFilter; import org.htmlparser.tags.FormTag; import org.htmlparser.tags.InputTag; import org.htmlparser.tags.TableRow; import org.htmlparser.tags.TableTag; import org.htmlparser.util.NodeList; import org.htmlparser.util.ParserException; import com.bao.lc.AppConfig; import com.bao.lc.bean.IDValuePair; import com.bao.lc.bean.ResultCode; import com.bao.lc.client.RequestBuilder; import com.bao.lc.client.utils.HttpClientUtils; import com.bao.lc.httpcommand.BasicHttpCommand; import com.bao.lc.httpcommand.params.HttpCommandPNames; import com.bao.lc.httpcommand.params.HttpCommandParams; import com.bao.lc.site.s2.ZyConstants; import com.bao.lc.util.AppUtils; import com.bao.lc.util.MiscUtils; public class SelectTicketDate extends BasicHttpCommand { private static Log log = LogFactory.getLog(SelectTicketDate.class); protected IDValuePair postExecute(Context context) throws Exception { return parse(context); } private IDValuePair parse(Context context) throws ParserException, IOException { HttpResponse rsp = HttpCommandParams.getResponse(context); String encoding = MapUtils.getString(context, ZyConstants.PARAM_RSP_ENCODING, "UTF-8"); String content = HttpClientUtils.saveToString(rsp.getEntity(), encoding); //1. Correct NodeList nodeList = getSelectTimeList(content, encoding); FormTag submitForm = null; List<InputTag> timeList = new ArrayList<InputTag>(2); List<Tag> regTimeList = new ArrayList<Tag>(0); for(int i = 0, size = nodeList.size(); i < size; i++) { Node node = nodeList.elementAt(i); if(!(node instanceof Tag)) { log.debug("Non-Tag node. " + node); continue; } Tag tagNode = (Tag) node; String name = tagNode.getAttribute("name"); if(StringUtils.equals(name, "ddlRegisterTime")) { regTimeList.add(tagNode); continue; } else if(StringUtils.equals(name, "ddlselect")) { timeList.add((InputTag) tagNode); continue; } String id = tagNode.getAttribute("id"); if(StringUtils.equals(id, "submitForm")) { submitForm = (FormTag) tagNode; } } if(log.isInfoEnabled()) { StringBuilder sb = new StringBuilder("[SelectTicket]: count="); sb.append(timeList.size()); for(int i = 0, size = timeList.size(); i < size; i++) { sb.append(" ").append(timeList.get(i).getAttribute("value")); } log.info(sb.toString()); } if(timeList.isEmpty()) { log.warn("[IMPORTANT]: time list is empty!"); IDValuePair rc = parseFailReason(context, content, encoding); if(rc != null) { return rc; } else { return ResultCode.RC_ZY_TIME_LIST_EMPTY; } } String diagDate = getFinalDiagDate(context, timeList); log.info("Target reg date: " + diagDate); if(diagDate == null) { log.warn("[IMPORTANT]: Can't find the target diag date."); IDValuePair rc = parseFailReason(context, content, encoding); if(rc != null) { return rc; } else { return ResultCode.RC_ZY_TARGET_DATE_NO_TICKET; } } String registertime = ""; for(int i = 0, size = regTimeList.size(); i < size; i++) { Tag tag = regTimeList.get(i); String date = tag.getAttribute("date"); if(StringUtils.equals(date, diagDate)) { registertime = tag.getAttribute("value"); break; } } Map<String, String> paramMap = new HashMap<String, String>(); NodeList submitInputs = submitForm.getFormInputs(); for(int i = 0, size = submitInputs.size(); i < size; i++) { InputTag input = (InputTag) submitInputs.elementAt(i); String name = input.getAttribute("name"); String value = input.getAttribute("value"); if(name.equals("diagdate")) { value = diagDate; } else if(name.equals("registertime")) { value = registertime; } paramMap.put(name, value); } if(log.isDebugEnabled()) { log.debug("ParamMap: " + MiscUtils.toString(paramMap)); } String method = submitForm.getFormMethod(); String location = submitForm.getFormLocation(); // Build the next URI URI requestURI = HttpCommandParams.getTargetRequestURI(context); // Next Request RequestBuilder rb = new RequestBuilder(); rb.method(method).reference(location).baseURI(requestURI); rb.paramMap(paramMap).encoding(encoding); HttpUriRequest nextRequest = rb.create(); context.put(HttpCommandPNames.TARGET_REQUEST, nextRequest); context.put(HttpCommandPNames.TARGET_REFERER, requestURI.toString()); return ResultCode.RC_OK; } private NodeList getSelectTimeList(String content, String encoding) throws ParserException { Parser parser = MiscUtils.createParser(content, encoding, log); // Set filters List<NodeFilter> predicates = new ArrayList<NodeFilter>(2); NodeFilter[] a = new NodeFilter[0]; // 1. Submit filters predicates.add(new HasAttributeFilter("id", "submitForm")); predicates.add(new NodeClassFilter(FormTag.class)); NodeFilter submitFormFilter = new AndFilter(predicates.toArray(a)); predicates.clear(); // 2. time select predicates.add(new HasAttributeFilter("name", "ddlselect")); predicates.add(new NodeClassFilter(InputTag.class)); NodeFilter selectFilter = new AndFilter(predicates.toArray(a)); predicates.clear(); // 3. predicates.add(new HasAttributeFilter("name", "ddlRegisterTime")); predicates.add(new HasAttributeFilter("date")); NodeFilter regTimeFilter = new AndFilter(predicates.toArray(a)); predicates.clear(); // 4. final filter predicates.add(submitFormFilter); predicates.add(selectFilter); predicates.add(regTimeFilter); NodeFilter finalFilter = new OrFilter(predicates.toArray(a)); predicates.clear(); // Fire! NodeList nodeList = parser.parse(finalFilter); return nodeList; } private Calendar toCalendar(String str) { String regex = "(\\d+?)-(\\d+?)-(\\d+?)\\|"; List<String> valueList = new ArrayList<String>(); int matchCount = MiscUtils.getRegexValue(str, regex, valueList, true, 0); if(matchCount != 1) { log.error("Failed to format str to date: " + str); return null; } return toCalendar(valueList, 1); } private Calendar toCalendar(List<String> valueList, int nStartIndex) { Calendar cal = Calendar.getInstance(); cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.YEAR, MiscUtils.toInt(valueList.get(nStartIndex++))); cal.set(Calendar.MONTH, MiscUtils.toInt(valueList.get(nStartIndex++)) - 1); cal.set(Calendar.DAY_OF_MONTH, MiscUtils.toInt(valueList.get(nStartIndex++))); return cal; } private String getFinalDiagDate(Context context, List<InputTag> timeList) { String diagDate = null; do { Calendar targetDay = (Calendar)context.get(ZyConstants.PARAM_TARGET_DAY); if(targetDay == null) { break; } log.info("Target day: " + MiscUtils.toString(targetDay)); for(int i = 0, size = timeList.size(); i < size; i++) { String value = timeList.get(i).getAttribute("value"); Calendar cal = toCalendar(value); if(cal != null && MiscUtils.isSameDay(targetDay, cal)) { diagDate = value; break; } } } while(false); if(diagDate != null) { return diagDate; } boolean fixedDay = MapUtils.getBooleanValue(context, ZyConstants.PARAM_FIXED_DAY, false); if(fixedDay) { log.info("Can't find the target day, stop to try."); return null; } log.info("Can't find the target day, try to find an alternative day."); for(int i = 0, size = timeList.size(); i < size; i++) { String value = timeList.get(i).getAttribute("value"); if(diagDate == null) { diagDate = value; } else { // pick up the big one if(diagDate.compareTo(value) < 0) { diagDate = value; } } } return diagDate; } private IDValuePair parseFailReason(Context context, String content, String encoding) throws ParserException { Parser parser = MiscUtils.createParser(content, encoding, log); // Set filters List<NodeFilter> predicates = new ArrayList<NodeFilter>(2); NodeFilter[] a = new NodeFilter[0]; // 1. data table filters predicates.add(new HasAttributeFilter("class", "datatable")); predicates.add(new NodeClassFilter(TableTag.class)); NodeFilter dataTableFilter = new AndFilter(predicates.toArray(a)); predicates.clear(); NodeList tableList = parser.parse(dataTableFilter); if(tableList.size() != 1) { log.error("The datatable doesn't exist. tableList.size=" + tableList.size()); return null; } // parse the table content TableTag dataTable = (TableTag)tableList.elementAt(0); TableRow[] rows = dataTable.getRows(); for(int i = 0; i < rows.length; i++) { String rowText = rows[i].toPlainTextString().trim(); rowText = StringUtils.replaceChars(rowText, "\r\n\t", null).trim(); if(rowText.isEmpty()) { continue; } if(i == 0 && rowText.contains(AppConfig.getInstance().getPropInternal("zy.datatable.h1"))) { continue; } if(i == 1 && rowText.contains(AppConfig.getInstance().getPropInternal("zy.datatable.h2"))) { continue; } log.debug("row[" + i + "]=" + rowText); IDValuePair rc = isFullOrOver(rowText); if(rc != null) { return rc; } Calendar targetDay = (Calendar)context.get(ZyConstants.PARAM_TARGET_DAY); rc = isStopToBook(targetDay, rowText); if(rc != null) { return rc; } } return null; } private IDValuePair isFullOrOver(String rowText) { int flags = Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNICODE_CASE; String regex = AppConfig.getInstance().getPropInternal("zy.datatable.full.regex1"); List<String> valueList = new ArrayList<String>(); int matchCount = MiscUtils.getRegexValue(rowText, regex, valueList, true, flags); if(matchCount == 1) { Calendar suggestRegDate = toCalendar(valueList, 2); Calendar today = Calendar.getInstance(); int c = MiscUtils.compareDay(today, suggestRegDate); if(c == 0) { log.debug("The register time is not reached yet."); return ResultCode.RC_ZY_REG_TIME_NOT_REACH_YET; } else if(c < 0) { log.info("All the ticket has been booked over really."); return ResultCode.RC_DOCTOR_REG_LIST_FULL; } } return null; } private IDValuePair isStopToBook(Calendar targetDay, String rowText) { if(targetDay == null) { return null; } int flags = Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNICODE_CASE; //STOP String regex = AppConfig.getInstance().getPropInternal("zy.datatable.stop.regex1"); List<String> valueList = new ArrayList<String>(); int matchCount = MiscUtils.getRegexValue(rowText, regex, valueList, true, flags); if(matchCount == 1) { Calendar stopDay = toCalendar(valueList, 2); if(MiscUtils.isSameDay(targetDay, stopDay)) { log.info("The ticket has been stopped to book."); return ResultCode.RC_ZY_TARGET_DATE_STOP_TO_BOOK; } } return null; } public static void main(String[] args) throws Exception { String[] names = {"1.html", "2.html"}; Context context = new ContextBase(); Calendar cal = Calendar.getInstance(); cal.set(2012, 3, 4); context.put(ZyConstants.PARAM_TARGET_DAY, cal); for(int i = 0; i < names.length; i++) { InputStream is = new FileInputStream(AppUtils.getTempFilePath(names[i])); String encoding = "UTF-8"; String content = IOUtils.toString(is, encoding); log.info("File: " + names[i]); SelectTicketDate theInstance = new SelectTicketDate(); IDValuePair rc = theInstance.parseFailReason(context, content, encoding); log.info("Result: " + rc); } } }