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.schedules.TrainScheduleManager;
025import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
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.getDropEngineTextColor() + ";\">");
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.getPickupEngineTextColor() + ";\">");
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.LOAD_TYPE)) {
216            return car.getLoadType();
217        } else if (attribute.equals(Setup.HAZARDOUS)) {
218            return car.isHazardous() ? Setup.getHazardousMsg() : ""; // NOI18N
219        } else if (attribute.equals(Setup.DROP_COMMENT)) {
220            return car.getDropComment();
221        } else if (attribute.equals(Setup.PICKUP_COMMENT)) {
222            return car.getPickupComment();
223        } else if (attribute.equals(Setup.KERNEL)) {
224            return car.getKernelName();
225        } else if (attribute.equals(Setup.KERNEL_SIZE)) {
226            if (car.getKernel() != null) {
227                return Integer.toString(car.getKernel().getSize());
228            } else {
229                return "";
230            }
231        } else if (attribute.equals(Setup.RWE)) {
232            if (!car.getReturnWhenEmptyDestinationName().isEmpty()) {
233                return String.format(locale, strings.getProperty("RWELocationAndTrack"), StringEscapeUtils
234                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationName()), StringEscapeUtils
235                        .escapeHtml4(car.getSplitReturnWhenEmptyDestinationTrackName()));
236            }
237            return ""; // NOI18N
238        } else if (attribute.equals(Setup.FINAL_DEST)) {
239            if (!car.getFinalDestinationName().isEmpty()) {
240                return String.format(locale, strings.getProperty("FinalDestinationLocation"), StringEscapeUtils
241                        .escapeHtml4(car.getSplitFinalDestinationName()));
242            }
243            return "";
244        } else if (attribute.equals(Setup.FINAL_DEST_TRACK)) {
245            if (!car.getFinalDestinationName().isEmpty()) {
246                return String.format(locale, strings.getProperty("FinalDestinationLocationAndTrack"), StringEscapeUtils
247                        .escapeHtml4(car.getSplitFinalDestinationName()), StringEscapeUtils
248                                .escapeHtml4(car.getSplitFinalDestinationTrackName()));
249            }
250            return "";
251        } else if (attribute.equals(Setup.DIVISION)) {
252            return car.getDivisionName();
253        } else if (attribute.equals(Setup.BLOCKING_ORDER)) {
254            if (car.isPassenger()) {
255                return Integer.toString(car.getBlocking());
256            }
257            return "";
258        }
259        return getRollingStockAttribute(car, attribute, isPickup, isLocal);
260    }
261
262    protected String getEngineAttribute(Engine engine, String attribute, boolean isPickup) {
263        if (attribute.equals(Setup.MODEL)) {
264            return engine.getModel();
265        }
266        if (attribute.equals(Setup.HP)) {
267            return engine.getHp();
268        }
269        if (attribute.equals(Setup.CONSIST)) {
270            return engine.getConsistName();
271        }
272        if (attribute.equals(Setup.DCC_ADDRESS)) {
273            return engine.getDccAddress();
274        }
275        return getRollingStockAttribute(engine, attribute, isPickup, false);
276    }
277
278    protected String getRollingStockAttribute(RollingStock rs, String attribute, boolean isPickup, boolean isLocal) {
279        if (attribute.equals(Setup.NUMBER)) {
280            return splitString(rs.getNumber());
281        } else if (attribute.equals(Setup.ROAD)) {
282            return StringEscapeUtils.escapeHtml4(rs.getRoadName().split(TrainCommon.HYPHEN)[0]);
283        } else if (attribute.equals(Setup.TYPE)) {
284            return rs.getTypeName().split(TrainCommon.HYPHEN)[0];
285        } else if (attribute.equals(Setup.LENGTH)) {
286            return rs.getLength();
287        } else if (attribute.equals(Setup.WEIGHT)) {
288            return Integer.toString(rs.getAdjustedWeightTons());
289        } else if (attribute.equals(Setup.COLOR)) {
290            return rs.getColor();
291        } else if (attribute.equals(Setup.LOCATION) && (isPickup || isLocal)
292                || (attribute.equals(Setup.TRACK) && isPickup)) {
293            if (rs.getTrack() != null) {
294                return String.format(locale, strings.getProperty("FromTrack"), StringEscapeUtils.escapeHtml4(rs
295                        .getSplitTrackName()));
296            }
297            return "";
298        } else if (attribute.equals(Setup.LOCATION) && !isPickup && !isLocal) {
299            return ""; // we don't have the car's origin, so nothing to return
300            // Note that the JSON database does have the car's origin, so this could be fixed.
301//            return String.format(locale, strings.getProperty("FromLocation"), StringEscapeUtils.escapeHtml4(rs
302//                    .getLocationName()));
303        } else if (attribute.equals(Setup.DESTINATION) && isPickup) {
304            return String.format(locale, strings.getProperty("ToLocation"), StringEscapeUtils
305                    .escapeHtml4(rs.getSplitDestinationName()));
306        } else if ((attribute.equals(Setup.DESTINATION) || attribute.equals(Setup.TRACK)) && !isPickup) {
307            return String.format(locale, strings.getProperty("ToTrack"), StringEscapeUtils.escapeHtml4(rs
308                    .getSplitDestinationTrackName()));
309        } else if (attribute.equals(Setup.DEST_TRACK)) {
310            return String.format(locale, strings.getProperty("ToLocationAndTrack"), StringEscapeUtils
311                    .escapeHtml4(rs.getSplitDestinationName()), StringEscapeUtils.escapeHtml4(rs
312                    .getSplitDestinationTrackName()));
313        } else if (attribute.equals(Setup.OWNER)) {
314            return StringEscapeUtils.escapeHtml4(rs.getOwnerName());
315        } else if (attribute.equals(Setup.COMMENT)) {
316            return StringEscapeUtils.escapeHtml4(rs.getComment());
317        } else if (attribute.equals(Setup.BLANK) || attribute.equals(Setup.NO_NUMBER)
318                || attribute.equals(Setup.NO_ROAD) || attribute.equals(Setup.NO_COLOR)
319                || attribute.equals(Setup.NO_DESTINATION) || attribute.equals(Setup.NO_DEST_TRACK)
320                || attribute.equals(Setup.NO_LOCATION) || attribute.equals(Setup.NO_TRACK)
321                || attribute.equals(Setup.TAB) || attribute.equals(Setup.TAB2) || attribute.equals(Setup.TAB3)) {
322            // attributes that don't print
323            return "";
324        }
325        return String.format(Bundle.getMessage(locale, "ErrorPrintOptions"), attribute); // something is isn't right!
326    }
327
328    protected String getTrackComments(RouteLocation location, List<Car> cars) {
329        StringBuilder builder = new StringBuilder();
330        if (location.getLocation() != null) {
331            List<Track> tracks = location.getLocation().getTracksByNameList(null);
332            for (Track track : tracks) {
333                // any pick ups or set outs to this track?
334                boolean pickup = false;
335                boolean setout = false;
336                for (Car car : cars) {
337                    if (car.getRouteLocation() == location && car.getTrack() != null && car.getTrack() == track) {
338                        pickup = true;
339                    }
340                    if (car.getRouteDestination() == location && car.getDestinationTrack() != null
341                            && car.getDestinationTrack() == track) {
342                        setout = true;
343                    }
344                }
345                // print the appropriate comment if there's one
346                if (pickup && setout && !track.getCommentBoth().isEmpty()) {
347                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
348                            .escapeHtml4(track.getCommentBothWithColor())));
349                } else if (pickup && !setout && !track.getCommentPickup().isEmpty()) {
350                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
351                            .escapeHtml4(track.getCommentPickupWithColor())));
352                } else if (!pickup && setout && !track.getCommentSetout().isEmpty()) {
353                    builder.append(String.format(locale, strings.getProperty("TrackComments"), StringEscapeUtils
354                            .escapeHtml4(track.getCommentSetoutWithColor())));
355                }
356            }
357        }
358        return builder.toString();
359    }
360
361    public String getValidity() {
362        if (Setup.isPrintTrainScheduleNameEnabled()) {
363            return String.format(locale, strings.getProperty("ManifestValidityWithSchedule"), getDate(true),
364                    InstanceManager.getDefault(TrainScheduleManager.class).getActiveSchedule().getName());
365        } else {
366            return String.format(locale, strings.getProperty("ManifestValidity"), getDate(true));
367        }
368    }
369
370    /**
371     * @param text Text with color tags needing conversion. See
372     *             TrainCommon.formatColorString(String text, Color color) Also
373     *             converts line feeds to HTLM
374     * @return HTML text with style color option
375     */
376    public static String convertToHTMLColor(String text) {
377        // convert line feeds
378        text = text.replace("\n", "<br>");
379
380        text = text.replace("&lt;FONT color=&quot;", "<p style=\"color: ");
381        text = text.replace("&quot;&gt;", "\">");
382        text = text.replace("&lt;/FONT&gt;", "");
383
384        return text;
385    }
386}