001package jmri.implementation;
002
003import com.fasterxml.jackson.databind.util.StdDateFormat;
004
005import java.text.*;
006import java.util.Calendar;
007import java.util.Date;
008import java.util.Locale;
009import java.util.Objects;
010import java.time.LocalDateTime;
011import java.time.ZoneId;
012import java.time.format.DateTimeParseException;
013import java.time.format.DateTimeFormatter;
014
015import javax.annotation.CheckForNull;
016
017import jmri.*;
018
019import org.jdom2.Element;
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023/**
024 * Concrete implementation of the {@link jmri.IdTag} interface for the Internal
025 * system.
026 * <hr>
027 * This file is part of JMRI.
028 * <p>
029 * JMRI is free software; you can redistribute it and/or modify it under the
030 * terms of version 2 of the GNU General Public License as published by the Free
031 * Software Foundation. See the "COPYING" file for a copy of this license.
032 * <p>
033 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
034 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
035 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
036 *
037 * @author Matthew Harris Copyright (C) 2011
038 * @since 2.11.4
039 */
040public class DefaultIdTag extends AbstractIdTag {
041
042    private int currentState = UNKNOWN;
043
044    public DefaultIdTag(String systemName) {
045        super(systemName);
046        setWhereLastSeen(null);
047    }
048
049    public DefaultIdTag(String systemName, String userName) {
050        super(systemName, userName);
051        setWhereLastSeen(null);
052    }
053
054    public final static String PROPERTY_WHEN_LAST_SEEN = "whenLastSeen";
055    public final static String PROPERTY_WHERE_LAST_SEEN = "whereLastSeen";
056
057    @Override
058    public int compareTo(NamedBean n2) {
059        Objects.requireNonNull(n2);
060        String o1 = this.getSystemName();
061        String o2 = n2.getSystemName();
062        int p1len = Manager.getSystemPrefixLength(o1);
063        int p2len = Manager.getSystemPrefixLength(o2);
064        int comp = o1.substring(0, p1len).compareTo(o2.substring(0, p2len));
065        if (comp != 0) 
066            return comp;
067        comp = o1.compareTo(o2);
068        return comp;
069    }
070
071    @Override
072    public final void setWhereLastSeen(@CheckForNull Reporter r) {
073        Reporter oldWhere = this.whereLastSeen;
074        Date oldWhen = this.whenLastSeen;
075        this.whereLastSeen = r;
076        if (r != null) {
077            this.whenLastSeen = getDateNow();
078        } else {
079            this.whenLastSeen = null;
080        }
081        setCurrentState(r != null ? SEEN : UNSEEN);
082        firePropertyChange(PROPERTY_WHERE_LAST_SEEN, oldWhere, this.whereLastSeen);
083        firePropertyChange(PROPERTY_WHEN_LAST_SEEN, oldWhen, this.whenLastSeen);
084    }
085
086    private Date getDateNow() {
087        return InstanceManager.getDefault(IdTagManager.class).isFastClockUsed()
088            ? InstanceManager.getDefault(ClockControl.class).getTime()
089            : Calendar.getInstance().getTime();
090    }
091
092    private void setCurrentState(int state) {
093        try {
094            setState(state);
095        } catch (JmriException ex) {
096            log.warn("Problem setting state of IdTag {} {}", getSystemName(),ex.getMessage());
097        }
098    }
099
100    @Override
101    public void setState(int s) throws JmriException {
102        this.currentState = s;
103    }
104
105    @Override
106    public int getState() {
107        return this.currentState;
108    }
109
110    @Override
111    public Element store(boolean storeState) {
112        Element e = new Element("idtag"); // NOI18N
113        e.addContent(new Element("systemName").addContent(this.mSystemName)); // NOI18N
114        String uName = this.getUserName();
115        if (uName != null && !uName.isEmpty()) {
116            e.addContent(new Element("userName").addContent(uName)); // NOI18N
117        }
118        String comment = this.getComment();
119        if ((comment != null) && (!comment.isEmpty())) {
120            e.addContent(new Element("comment").addContent(comment)); // NOI18N
121        }
122        Reporter whereLast = this.getWhereLastSeen();
123        if (whereLast != null && storeState) {
124            e.addContent(new Element(PROPERTY_WHERE_LAST_SEEN).addContent(whereLast.getSystemName()));
125        }
126        if (this.getWhenLastSeen() != null && storeState) {
127            e.addContent(new Element(PROPERTY_WHEN_LAST_SEEN).addContent(new StdDateFormat().format(this.getWhenLastSeen())));
128        }
129        return e;
130    }
131
132    /**
133     * Load an idtag xml element.
134     * whenLastSeen formats accepted JMRI 5.3.6 include
135     * yyyy-MM-dd'T'HH:mm:ss.SSSX
136     * yyyy-MM-dd'T'HH:mm:ss.SSS
137     * EEE, dd MMM yyyy HH:mm:ss zzz
138     * 
139     * @param e element to load.
140     */
141    @Override
142    public void load(Element e) {
143        if (e.getName().equals("idtag")) { // NOI18N
144            log.debug("Load IdTag element for {}", this.getSystemName());
145            if (e.getChild("userName") != null) { // NOI18N
146                this.setUserName(e.getChild("userName").getText()); // NOI18N
147            }
148            if (e.getChild("comment") != null) { // NOI18N
149                this.setComment(e.getChild("comment").getText()); // NOI18N
150            }
151            if (e.getChild(PROPERTY_WHERE_LAST_SEEN) != null) {
152                try {
153                    Reporter r = InstanceManager.getDefault(ReporterManager.class)
154                                    .provideReporter(e.getChild(PROPERTY_WHERE_LAST_SEEN).getText());
155                    this.setWhereLastSeen(r);
156                    this.whenLastSeen = null;
157                } catch (IllegalArgumentException ex) {
158                    log.warn("Failed to provide Reporter \"{}\" in load of \"{}\"", e.getChild(PROPERTY_WHERE_LAST_SEEN).getText(), getDisplayName());
159                }
160            }
161            if (e.getChild(PROPERTY_WHEN_LAST_SEEN) != null) {
162                String lastSeenText = e.getChildText(PROPERTY_WHEN_LAST_SEEN);
163                log.debug("Loading {} When Last Seen: {}", getDisplayName(), lastSeenText);
164                try { // parse using ISO 8601 date format
165                    this.whenLastSeen = new StdDateFormat().parse(lastSeenText);
166                } catch (ParseException ex) {
167                    log.debug("ParseException in whenLastSeen ISO attempt: \"{}\"", lastSeenText, ex);
168                    // next, try parse using how it was saved by JMRI < 5.3.5
169                    try {
170                        // the result of this string may change between Java versions
171                        this.whenLastSeen = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).parse(lastSeenText);
172                    } catch (ParseException ex2) {
173                        // next, try parse fixed format JMRI < 5.3.5, Java8 Locale US
174                        try {
175                            DateTimeFormatter previousUsFormatter =
176                            DateTimeFormatter.ofPattern("MMM d, yyyy, h:mm:ss a", Locale.US);
177                            LocalDateTime ldt = LocalDateTime.parse(lastSeenText, previousUsFormatter);
178                            this.whenLastSeen = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
179                        } catch (DateTimeParseException ex3) {
180                            log.warn("During load of IdTag \"{}\" {}", getDisplayName(), ex.getMessage());
181                        }
182                    }
183                }
184            }
185        } else {
186            log.error("Not an IdTag element: \"{}\" for Tag \"{}\"", e.getName(), this.getDisplayName());
187        }
188    }
189
190    private static final Logger log = LoggerFactory.getLogger(DefaultIdTag.class);
191
192}