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