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