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("<span style=\"color: " + Setup.getPickupTextColor() + ";\">");
104        builder.append(Setup.getPickupCarPrefix()).append(" ");
105        // count the number of utility cars
106        if (count != 0) {
107            builder.append(count);
108        }
109        for (String attribute : format) {
110            builder.append(
111                    String.format(locale, strings.getProperty("Attribute"), getCarAttribute(car, attribute, PICKUP,
112                            !LOCAL), attribute.toLowerCase())).append(" "); // NOI18N
113        }
114        log.debug("Picking up car {}", builder);
115        return String.format(locale, strings.getProperty(this.resourcePrefix + "PickUpCar"), builder.toString()); // NOI18N
116    }
117
118    protected String dropCar(Car car, String[] format, boolean isLocal) {
119        return dropCar(car, 0, format, isLocal);
120    }
121
122    protected String dropCar(Car car, int count, String[] format, boolean isLocal) {
123        StringBuilder builder = new StringBuilder();
124        if (!isLocal) {
125            builder.append("<span style=\"color: " + Setup.getDropTextColor() + ";\">");
126            builder.append(Setup.getDropCarPrefix()).append(" ");
127        } else {
128            builder.append("<span style=\"color: " + Setup.getLocalTextColor() + ";\">");
129            builder.append(Setup.getLocalPrefix()).append(" ");
130        }
131        // count the number of utility cars
132        if (count != 0) {
133            builder.append(count);
134        }
135        for (String attribute : format) {
136            builder.append(
137                    String.format(locale, strings.getProperty("Attribute"), getCarAttribute(car, attribute, !PICKUP,
138                            isLocal), attribute.toLowerCase())).append(" "); // NOI18N
139        }
140        log.debug("Dropping {}car {}", (isLocal) ? "local " : "", builder);
141        if (!isLocal) {
142            return String.format(locale, strings.getProperty(this.resourcePrefix + "DropCar"), builder.toString()); // NOI18N
143        } else {
144            return String.format(locale, strings.getProperty(this.resourcePrefix + "LocalCar"), builder.toString()); // NOI18N
145        }
146    }
147
148    protected String engineChange(RouteLocation rl, int legOptions) {
149        if ((legOptions & Train.HELPER_ENGINES) == Train.HELPER_ENGINES) {
150            return String.format(strings.getProperty("AddHelpersAt"), rl.getSplitName()); // NOI18N
151        } else if ((legOptions & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES
152                && ((legOptions & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || (legOptions & Train.ADD_CABOOSE) == Train.ADD_CABOOSE)) {
153            return String.format(strings.getProperty("LocoAndCabooseChangeAt"), rl.getSplitName()); // NOI18N
154        } else if ((legOptions & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES) {
155            return String.format(strings.getProperty("LocoChangeAt"), rl.getSplitName()); // NOI18N
156        } else if ((legOptions & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE
157                || (legOptions & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) {
158            return String.format(strings.getProperty("CabooseChangeAt"), rl.getSplitName()); // NOI18N
159        }
160        return "";
161    }
162
163    protected String dropEngines(List<Engine> engines, RouteLocation location) {
164        StringBuilder builder = new StringBuilder();
165        for (Engine engine : engines) {
166            if (engine.getRouteDestination().equals(location)) {
167                builder.append(dropEngine(engine));
168            }
169        }
170        return String.format(strings.getProperty("EnginesList"), builder.toString());
171    }
172
173    @Override
174    public String dropEngine(Engine engine) {
175        StringBuilder builder = new StringBuilder();
176        builder.append("<span style=\"color: " + Setup.getDropTextColor() + ";\">");
177        builder.append(Setup.getDropEnginePrefix()).append(" ");
178        for (String attribute : Setup.getDropEngineMessageFormat()) {
179            builder.append(
180                    String.format(locale, strings.getProperty("Attribute"),
181                            getEngineAttribute(engine, attribute, false), attribute.toLowerCase())).append(" ");
182        }
183        log.debug("Drop engine: {}", builder);
184        return String.format(locale, strings.getProperty(this.resourcePrefix + "DropEngine"), builder.toString());
185    }
186
187    protected String pickupEngines(List<Engine> engines, RouteLocation location) {
188        StringBuilder builder = new StringBuilder();
189        for (Engine engine : engines) {
190            if (engine.getRouteLocation().equals(location) && !engine.getTrackName().isEmpty()) {
191                builder.append(pickupEngine(engine));
192            }
193        }
194        return String.format(locale, strings.getProperty("EnginesList"), builder.toString());
195    }
196
197    @Override
198    public String pickupEngine(Engine engine) {
199        StringBuilder builder = new StringBuilder();
200        builder.append("<span style=\"color: " + Setup.getPickupTextColor() + ";\">");
201        builder.append(Setup.getPickupEnginePrefix()).append(" ");
202        for (String attribute : Setup.getPickupEngineMessageFormat()) {
203            builder.append(
204                    String.format(locale, strings.getProperty("Attribute"),
205                            getEngineAttribute(engine, attribute, true), attribute.toLowerCase())).append(" ");
206        }
207        log.debug("Picking up engine: {}", builder);
208        return String.format(locale, strings.getProperty(this.resourcePrefix + "PickUpEngine"), builder.toString());
209    }
210
211    protected String getCarAttribute(Car car, String attribute, boolean isPickup, boolean isLocal) {
212        if (attribute.equals(Setup.LOAD)) {
213            return (car.isCaboose() || car.isPassenger()) ? ""
214                    : StringEscapeUtils.escapeHtml4(car.getLoadName().split(TrainCommon.HYPHEN)[0]); // NOI18N
215        } else if (attribute.equals(Setup.HAZARDOUS)) {
216            return car.isHazardous() ? Setup.getHazardousMsg() : ""; // NOI18N
217        } else if (attribute.equals(Setup.DROP_COMMENT)) {
218            return car.getDropComment();
219        } else if (attribute.equals(Setup.PICKUP_COMMENT)) {
220            return car.getPickupComment();
221        } else if (attribute.equals(Setup.KERNEL)) {
222            return car.getKernelName();
223        } else if (attribute.equals(Setup.RWE)) {
224            if (!car.getReturnWhenEmptyDestinationName().isEmpty()) {
225                return String.format(locale, strings.getProperty("RWELocationAndTrack"), StringEscapeUtils
226                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationName()), StringEscapeUtils
227                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationTrackName()));
228            }
229            return ""; // NOI18N
230        } else if (attribute.equals(Setup.FINAL_DEST)) {
231            if (!car.getFinalDestinationName().isEmpty()) {
232                return String.format(locale, strings.getProperty("FinalDestinationLocation"), StringEscapeUtils
233                        .escapeHtml4(car.getSplitFinalDestinationName()));
234            }
235            return "";
236        } else if (attribute.equals(Setup.FINAL_DEST_TRACK)) {
237            if (!car.getFinalDestinationName().isEmpty()) {
238                return String.format(locale, strings.getProperty("FinalDestinationLocationAndTrack"), StringEscapeUtils
239                        .escapeHtml4(car.getSplitFinalDestinationName()), StringEscapeUtils
240                        .escapeHtml4(car.getSplitFinalDestinationTrackName()));
241            }
242            return "";
243        }
244        return getRollingStockAttribute(car, attribute, isPickup, isLocal);
245    }
246
247    protected String getEngineAttribute(Engine engine, String attribute, boolean isPickup) {
248        if (attribute.equals(Setup.MODEL)) {
249            return engine.getModel();
250        }
251        if (attribute.equals(Setup.HP)) {
252            return engine.getHp();
253        }
254        if (attribute.equals(Setup.CONSIST)) {
255            return engine.getConsistName();
256        }
257        if (attribute.equals(Setup.DCC_ADDRESS)) {
258            return engine.getDccAddress();
259        }
260        return getRollingStockAttribute(engine, attribute, isPickup, false);
261    }
262
263    protected String getRollingStockAttribute(RollingStock rs, String attribute, boolean isPickup, boolean isLocal) {
264        if (attribute.equals(Setup.NUMBER)) {
265            return splitString(rs.getNumber());
266        } else if (attribute.equals(Setup.ROAD)) {
267            return StringEscapeUtils.escapeHtml4(rs.getRoadName().split(TrainCommon.HYPHEN)[0]);
268        } else if (attribute.equals(Setup.TYPE)) {
269            return rs.getTypeName().split(TrainCommon.HYPHEN)[0];
270        } else if (attribute.equals(Setup.LENGTH)) {
271            return rs.getLength();
272        } else if (attribute.equals(Setup.WEIGHT)) {
273            return Integer.toString(rs.getAdjustedWeightTons());
274        } else if (attribute.equals(Setup.COLOR)) {
275            return rs.getColor();
276        } else if (attribute.equals(Setup.LOCATION) && (isPickup || isLocal)
277                || (attribute.equals(Setup.TRACK) && isPickup)) {
278            if (rs.getTrack() != null) {
279                return String.format(locale, strings.getProperty("FromTrack"), StringEscapeUtils.escapeHtml4(rs
280                        .getSplitTrackName()));
281            }
282            return "";
283        } else if (attribute.equals(Setup.LOCATION) && !isPickup && !isLocal) {
284            return ""; // we don't have the car's origin, so nothing to return
285            // Note that the JSON database does have the car's origin, so this could be fixed.
286//            return String.format(locale, strings.getProperty("FromLocation"), StringEscapeUtils.escapeHtml4(rs
287//                    .getLocationName()));
288        } else if (attribute.equals(Setup.DESTINATION) && isPickup) {
289            return String.format(locale, strings.getProperty("ToLocation"), StringEscapeUtils
290                    .escapeHtml4(rs.getSplitDestinationName()));
291        } else if ((attribute.equals(Setup.DESTINATION) || attribute.equals(Setup.TRACK)) && !isPickup) {
292            return String.format(locale, strings.getProperty("ToTrack"), StringEscapeUtils.escapeHtml4(rs
293                    .getSplitDestinationTrackName()));
294        } else if (attribute.equals(Setup.DEST_TRACK)) {
295            return String.format(locale, strings.getProperty("ToLocationAndTrack"), StringEscapeUtils
296                    .escapeHtml4(rs.getSplitDestinationName()), StringEscapeUtils.escapeHtml4(rs
297                    .getSplitDestinationTrackName()));
298        } else if (attribute.equals(Setup.OWNER)) {
299            return StringEscapeUtils.escapeHtml4(rs.getOwnerName());
300        } else if (attribute.equals(Setup.COMMENT)) {
301            return StringEscapeUtils.escapeHtml4(rs.getComment());
302        } else if (attribute.equals(Setup.BLANK) || attribute.equals(Setup.NO_NUMBER)
303                || attribute.equals(Setup.NO_ROAD) || attribute.equals(Setup.NO_COLOR)
304                || attribute.equals(Setup.NO_DESTINATION) || attribute.equals(Setup.NO_DEST_TRACK)
305                || attribute.equals(Setup.NO_LOCATION) || attribute.equals(Setup.NO_TRACK)
306                || attribute.equals(Setup.TAB) || attribute.equals(Setup.TAB2) || attribute.equals(Setup.TAB3)) {
307            // attributes that don't print
308            return "";
309        }
310        return String.format(Bundle.getMessage(locale, "ErrorPrintOptions"), attribute); // something is isn't right!
311    }
312
313    protected String getTrackComments(RouteLocation location, List<Car> cars) {
314        StringBuilder builder = new StringBuilder();
315        if (location.getLocation() != null) {
316            List<Track> tracks = location.getLocation().getTracksByNameList(null);
317            for (Track track : tracks) {
318                // any pick ups or set outs to this track?
319                boolean pickup = false;
320                boolean setout = false;
321                for (Car car : cars) {
322                    if (car.getRouteLocation() == location && car.getTrack() != null && car.getTrack() == track) {
323                        pickup = true;
324                    }
325                    if (car.getRouteDestination() == location && car.getDestinationTrack() != null
326                            && car.getDestinationTrack() == track) {
327                        setout = true;
328                    }
329                }
330                // print the appropriate comment if there's one
331                if (pickup && setout && !track.getCommentBoth().isEmpty()) {
332                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
333                            .escapeHtml4(track.getCommentBothWithColor())));
334                } else if (pickup && !setout && !track.getCommentPickup().isEmpty()) {
335                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
336                            .escapeHtml4(track.getCommentPickupWithColor())));
337                } else if (!pickup && setout && !track.getCommentSetout().isEmpty()) {
338                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
339                            .escapeHtml4(track.getCommentSetoutWithColor())));
340                }
341            }
342        }
343        return builder.toString();
344    }
345
346    public String getValidity() {
347        if (Setup.isPrintTrainScheduleNameEnabled()) {
348            return String.format(locale, strings.getProperty("ManifestValidityWithSchedule"), getDate(true),
349                    InstanceManager.getDefault(TrainScheduleManager.class).getActiveSchedule().getName());
350        } else {
351            return String.format(locale, strings.getProperty("ManifestValidity"), getDate(true));
352        }
353    }
354
355    /**
356     * @param text Text with color tags needing conversion. See
357     *             TrainCommon.formatColorString(String text, Color color) Also
358     *             converts line feeds to HTLM
359     * @return HTML text with style color option
360     */
361    public static String convertToHTMLColor(String text) {
362        // convert line feeds
363        text = text.replace("\n", "<br>");
364
365        text = text.replace("&lt;FONT color=&quot;", "<p style=\"color: ");
366        text = text.replace("&quot;&gt;", "\">");
367        text = text.replace("&lt;/FONT&gt;", "");
368
369        return text;
370    }
371}