001/*
002 * To change this license header, choose License Headers in Project Properties.
003 * To change this template file, choose Tools | Templates
004 * and open the template in the editor.
005 */
006package jmri.web.servlet.operations;
007
008import java.io.FileInputStream;
009import java.io.IOException;
010import java.util.*;
011
012import org.apache.commons.text.StringEscapeUtils;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import jmri.InstanceManager;
017import jmri.jmrit.operations.locations.Track;
018import jmri.jmrit.operations.rollingstock.RollingStock;
019import jmri.jmrit.operations.rollingstock.cars.Car;
020import jmri.jmrit.operations.rollingstock.engines.Engine;
021import jmri.jmrit.operations.routes.RouteLocation;
022import jmri.jmrit.operations.setup.Setup;
023import jmri.jmrit.operations.trains.Train;
024import jmri.jmrit.operations.trains.TrainCommon;
025import jmri.jmrit.operations.trains.schedules.TrainScheduleManager;
026
027/**
028 *
029 * @author Randall Wood
030 */
031public class HtmlTrainCommon extends TrainCommon {
032
033    protected final Properties strings = new Properties();
034    protected final Locale locale;
035    protected final Train train;
036    protected String resourcePrefix;
037
038    protected enum ShowLocation {
039
040        location, track, both;
041    }
042
043    static private final Logger log = LoggerFactory.getLogger(HtmlTrainCommon.class);
044
045    public HtmlTrainCommon(Locale locale, Train train) throws IOException {
046        this.locale = locale;
047        this.train = train;
048        FileInputStream is = null;
049        try {
050            is = new FileInputStream(Bundle.getMessage(locale, "ManifestStrings.properties"));
051            strings.load(is);
052            is.close();
053        }
054        catch (IOException ex) {
055            if (is != null) {
056                is.close();
057            }
058            throw ex;
059        }
060    }
061
062    public String pickupUtilityCars(List<Car> cars, Car car, boolean isManifest) {
063        // list utility cars by type, track, length, and load
064        String[] messageFormat;
065        if (isManifest) {
066            messageFormat = Setup.getPickupUtilityManifestMessageFormat();
067        } else {
068            messageFormat = Setup.getPickupUtilitySwitchListMessageFormat();
069        }
070        int count = countUtilityCars(messageFormat, cars, car, PICKUP);
071        if (count == 0) {
072            return ""; // already printed out this car type
073        }
074        return pickUpCar(car, count, messageFormat);
075    }
076
077    protected String setoutUtilityCars(List<Car> cars, Car car, boolean isManifest) {
078        boolean isLocal = car.isLocalMove();
079        if (Setup.isSwitchListFormatSameAsManifest()) {
080            isManifest = true;
081        }
082        String[] messageFormat = Setup.getDropUtilityManifestMessageFormat();
083        if (isLocal && isManifest) {
084            messageFormat = Setup.getLocalUtilityManifestMessageFormat();
085        } else if (isLocal && !isManifest) {
086            messageFormat = Setup.getLocalUtilitySwitchListMessageFormat();
087        } else if (!isLocal && !isManifest) {
088            messageFormat = Setup.getDropUtilitySwitchListMessageFormat();
089        }
090        int count = countUtilityCars(messageFormat, cars, car, !PICKUP);
091        if (count == 0) {
092            return ""; // already printed out this car type
093        }
094        return dropCar(car, count, messageFormat, isLocal);
095    }
096
097    protected String pickUpCar(Car car, String[] format) {
098        return pickUpCar(car, 0, format);
099    }
100
101    protected String pickUpCar(Car car, int count, String[] format) {
102        StringBuilder builder = new StringBuilder();
103        builder.append(Setup.getPickupCarPrefix()).append(" ");
104        // count the number of utility cars
105        if (count != 0) {
106            builder.append(count);
107        }
108        for (String attribute : format) {
109            builder.append(
110                    String.format(locale, strings.getProperty("Attribute"), getCarAttribute(car, attribute, PICKUP,
111                            !LOCAL), attribute.toLowerCase())).append(" "); // NOI18N
112        }
113        log.debug("Picking up car {}", builder);
114        return String.format(locale, strings.getProperty(this.resourcePrefix + "PickUpCar"), builder.toString()); // NOI18N
115    }
116
117    protected String dropCar(Car car, String[] format, boolean isLocal) {
118        return dropCar(car, 0, format, isLocal);
119    }
120
121    protected String dropCar(Car car, int count, String[] format, boolean isLocal) {
122        StringBuilder builder = new StringBuilder();
123        if (!isLocal) {
124            builder.append(Setup.getDropCarPrefix()).append(" ");
125        } else {
126            builder.append(Setup.getLocalPrefix()).append(" ");
127        }
128        // count the number of utility cars
129        if (count != 0) {
130            builder.append(count);
131        }
132        for (String attribute : format) {
133            builder.append(
134                    String.format(locale, strings.getProperty("Attribute"), getCarAttribute(car, attribute, !PICKUP,
135                            isLocal), attribute.toLowerCase())).append(" "); // NOI18N
136        }
137        log.debug("Dropping {}car {}", (isLocal) ? "local " : "", builder);
138        if (!isLocal) {
139            return String.format(locale, strings.getProperty(this.resourcePrefix + "DropCar"), builder.toString()); // NOI18N
140        } else {
141            return String.format(locale, strings.getProperty(this.resourcePrefix + "LocalCar"), builder.toString()); // NOI18N
142        }
143    }
144
145    protected String engineChange(RouteLocation rl, int legOptions) {
146        if ((legOptions & Train.HELPER_ENGINES) == Train.HELPER_ENGINES) {
147            return String.format(strings.getProperty("AddHelpersAt"), rl.getSplitName()); // NOI18N
148        } else if ((legOptions & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES
149                && ((legOptions & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || (legOptions & Train.ADD_CABOOSE) == Train.ADD_CABOOSE)) {
150            return String.format(strings.getProperty("LocoAndCabooseChangeAt"), rl.getSplitName()); // NOI18N
151        } else if ((legOptions & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES) {
152            return String.format(strings.getProperty("LocoChangeAt"), rl.getSplitName()); // NOI18N
153        } else if ((legOptions & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE
154                || (legOptions & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) {
155            return String.format(strings.getProperty("CabooseChangeAt"), rl.getSplitName()); // NOI18N
156        }
157        return "";
158    }
159
160    protected String dropEngines(List<Engine> engines, RouteLocation location) {
161        StringBuilder builder = new StringBuilder();
162        for (Engine engine : engines) {
163            if (engine.getRouteDestination().equals(location)) {
164                builder.append(dropEngine(engine));
165            }
166        }
167        return String.format(strings.getProperty("EnginesList"), builder.toString());
168    }
169
170    @Override
171    public String dropEngine(Engine engine) {
172        StringBuilder builder = new StringBuilder();
173        builder.append(Setup.getDropEnginePrefix()).append(" ");
174        for (String attribute : Setup.getDropEngineMessageFormat()) {
175            builder.append(
176                    String.format(locale, strings.getProperty("Attribute"),
177                            getEngineAttribute(engine, attribute, false), attribute.toLowerCase())).append(" ");
178        }
179        log.debug("Drop engine: {}", builder);
180        return String.format(locale, strings.getProperty(this.resourcePrefix + "DropEngine"), builder.toString());
181    }
182
183    protected String pickupEngines(List<Engine> engines, RouteLocation location) {
184        StringBuilder builder = new StringBuilder();
185        for (Engine engine : engines) {
186            if (engine.getRouteLocation().equals(location) && !engine.getTrackName().isEmpty()) {
187                builder.append(pickupEngine(engine));
188            }
189        }
190        return String.format(locale, strings.getProperty("EnginesList"), builder.toString());
191    }
192
193    @Override
194    public String pickupEngine(Engine engine) {
195        StringBuilder builder = new StringBuilder();
196        builder.append(Setup.getPickupEnginePrefix()).append(" ");
197        for (String attribute : Setup.getPickupEngineMessageFormat()) {
198            builder.append(
199                    String.format(locale, strings.getProperty("Attribute"),
200                            getEngineAttribute(engine, attribute, true), attribute.toLowerCase())).append(" ");
201        }
202        log.debug("Picking up engine: {}", builder);
203        return String.format(locale, strings.getProperty(this.resourcePrefix + "PickUpEngine"), builder.toString());
204    }
205
206    protected String getCarAttribute(Car car, String attribute, boolean isPickup, boolean isLocal) {
207        if (attribute.equals(Setup.LOAD)) {
208            return (car.isCaboose() || car.isPassenger()) ? "" : StringEscapeUtils.escapeHtml4(car.getLoadName()); // NOI18N
209        } else if (attribute.equals(Setup.HAZARDOUS)) {
210            return car.isHazardous() ? Setup.getHazardousMsg() : ""; // NOI18N
211        } else if (attribute.equals(Setup.DROP_COMMENT)) {
212            return car.getDropComment();
213        } else if (attribute.equals(Setup.PICKUP_COMMENT)) {
214            return car.getPickupComment();
215        } else if (attribute.equals(Setup.KERNEL)) {
216            return car.getKernelName();
217        } else if (attribute.equals(Setup.RWE)) {
218            if (!car.getReturnWhenEmptyDestinationName().isEmpty()) {
219                return String.format(locale, strings.getProperty("RWELocationAndTrack"), StringEscapeUtils
220                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationName()), StringEscapeUtils
221                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationTrackName()));
222            }
223            return ""; // NOI18N
224        } else if (attribute.equals(Setup.FINAL_DEST)) {
225            if (!car.getFinalDestinationName().isEmpty()) {
226                return String.format(locale, strings.getProperty("FinalDestinationLocation"), StringEscapeUtils
227                        .escapeHtml4(car.getSplitFinalDestinationName()));
228            }
229            return "";
230        } else if (attribute.equals(Setup.FINAL_DEST_TRACK)) {
231            if (!car.getFinalDestinationName().isEmpty()) {
232                return String.format(locale, strings.getProperty("FinalDestinationLocationAndTrack"), StringEscapeUtils
233                        .escapeHtml4(car.getSplitFinalDestinationName()), StringEscapeUtils
234                        .escapeHtml4(car.getSplitFinalDestinationTrackName()));
235            }
236            return "";
237        }
238        return getRollingStockAttribute(car, attribute, isPickup, isLocal);
239    }
240
241    protected String getEngineAttribute(Engine engine, String attribute, boolean isPickup) {
242        if (attribute.equals(Setup.MODEL)) {
243            return engine.getModel();
244        }
245        if (attribute.equals(Setup.CONSIST)) {
246            return engine.getConsistName();
247        }
248        if (attribute.equals(Setup.DCC_ADDRESS)) {
249            return engine.getDccAddress();
250        }
251        return getRollingStockAttribute(engine, attribute, isPickup, false);
252    }
253
254    protected String getRollingStockAttribute(RollingStock rs, String attribute, boolean isPickup, boolean isLocal) {
255        if (attribute.equals(Setup.NUMBER)) {
256            return splitString(rs.getNumber());
257        } else if (attribute.equals(Setup.ROAD)) {
258            return StringEscapeUtils.escapeHtml4(rs.getRoadName().split(TrainCommon.HYPHEN)[0]);
259        } else if (attribute.equals(Setup.TYPE)) {
260            return rs.getTypeName().split(TrainCommon.HYPHEN)[0];
261        } else if (attribute.equals(Setup.LENGTH)) {
262            return rs.getLength();
263        } else if (attribute.equals(Setup.COLOR)) {
264            return rs.getColor();
265        } else if (attribute.equals(Setup.LOCATION) && (isPickup || isLocal)
266                || (attribute.equals(Setup.TRACK) && isPickup)) {
267            if (rs.getTrack() != null) {
268                return String.format(locale, strings.getProperty("FromTrack"), StringEscapeUtils.escapeHtml4(rs
269                        .getSplitTrackName()));
270            }
271            return "";
272        } else if (attribute.equals(Setup.LOCATION) && !isPickup && !isLocal) {
273            return ""; // we don't have the car's origin, so nothing to return
274            // Note that the JSON database does have the car's origin, so this could be fixed.
275//            return String.format(locale, strings.getProperty("FromLocation"), StringEscapeUtils.escapeHtml4(rs
276//                    .getLocationName()));
277        } else if (attribute.equals(Setup.DESTINATION) && isPickup) {
278            return String.format(locale, strings.getProperty("ToLocation"), StringEscapeUtils
279                    .escapeHtml4(rs.getSplitDestinationName()));
280        } else if ((attribute.equals(Setup.DESTINATION) || attribute.equals(Setup.TRACK)) && !isPickup) {
281            return String.format(locale, strings.getProperty("ToTrack"), StringEscapeUtils.escapeHtml4(rs
282                    .getSplitDestinationTrackName()));
283        } else if (attribute.equals(Setup.DEST_TRACK)) {
284            return String.format(locale, strings.getProperty("ToLocationAndTrack"), StringEscapeUtils
285                    .escapeHtml4(rs.getSplitDestinationName()), StringEscapeUtils.escapeHtml4(rs
286                    .getSplitDestinationTrackName()));
287        } else if (attribute.equals(Setup.OWNER)) {
288            return StringEscapeUtils.escapeHtml4(rs.getOwnerName());
289        } else if (attribute.equals(Setup.COMMENT)) {
290            return StringEscapeUtils.escapeHtml4(rs.getComment());
291        } else if (attribute.equals(Setup.BLANK) || attribute.equals(Setup.NO_NUMBER)
292                || attribute.equals(Setup.NO_ROAD) || attribute.equals(Setup.NO_COLOR)
293                || attribute.equals(Setup.NO_DESTINATION) || attribute.equals(Setup.NO_DEST_TRACK)
294                || attribute.equals(Setup.NO_LOCATION) || attribute.equals(Setup.NO_TRACK)
295                || attribute.equals(Setup.TAB) || attribute.equals(Setup.TAB2) || attribute.equals(Setup.TAB3)) {
296            // attributes that don't print
297            return "";
298        }
299        return String.format(Bundle.getMessage(locale, "ErrorPrintOptions"), attribute); // something is isn't right!
300    }
301
302    protected String getTrackComments(RouteLocation location, List<Car> cars) {
303        StringBuilder builder = new StringBuilder();
304        if (location.getLocation() != null) {
305            List<Track> tracks = location.getLocation().getTracksByNameList(null);
306            for (Track track : tracks) {
307                // any pick ups or set outs to this track?
308                boolean pickup = false;
309                boolean setout = false;
310                for (Car car : cars) {
311                    if (car.getRouteLocation() == location && car.getTrack() != null && car.getTrack() == track) {
312                        pickup = true;
313                    }
314                    if (car.getRouteDestination() == location && car.getDestinationTrack() != null
315                            && car.getDestinationTrack() == track) {
316                        setout = true;
317                    }
318                }
319                // print the appropriate comment if there's one
320                if (pickup && setout && !track.getCommentBoth().isEmpty()) {
321                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
322                            .escapeHtml4(track.getCommentBothWithColor())));
323                } else if (pickup && !setout && !track.getCommentPickup().isEmpty()) {
324                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
325                            .escapeHtml4(track.getCommentPickupWithColor())));
326                } else if (!pickup && setout && !track.getCommentSetout().isEmpty()) {
327                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
328                            .escapeHtml4(track.getCommentSetoutWithColor())));
329                }
330            }
331        }
332        return builder.toString();
333    }
334
335    public String getValidity() {
336        if (Setup.isPrintTrainScheduleNameEnabled()) {
337            return String.format(locale, strings.getProperty("ManifestValidityWithSchedule"), getDate(true),
338                    InstanceManager.getDefault(TrainScheduleManager.class).getActiveSchedule().getName());
339        } else {
340            return String.format(locale, strings.getProperty("ManifestValidity"), getDate(true));
341        }
342    }
343
344    /**
345     * @param text Text with color tags needing conversion. See
346     *             TrainCommon.formatColorString(String text, Color color) Also
347     *             converts line feeds to HTLM
348     * @return HTML text with style color option
349     */
350    public static String convertToHTMLColor(String text) {
351        // convert line feeds
352        text = text.replace("\n", "<br>");
353
354        text = text.replace("&lt;FONT color=&quot;", "<p style=\"color: ");
355        text = text.replace("&quot;&gt;", "\">");
356        text = text.replace("&lt;/FONT&gt;", "");
357
358        return text;
359    }
360}