1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package tools.gsf.facade.mda;
17
18 import COM.FutureTense.Interfaces.FTValList;
19 import COM.FutureTense.Interfaces.ICS;
20 import com.fatwire.assetapi.data.AssetId;
21 import com.fatwire.cs.core.db.PreparedStmt;
22 import com.fatwire.cs.core.db.StatementParam;
23 import com.fatwire.mda.Dimension;
24 import com.fatwire.mda.DimensionException;
25 import com.fatwire.mda.DimensionFilterInstance;
26 import com.fatwire.mda.DimensionManager;
27 import com.fatwire.mda.DimensionSetInstance;
28 import com.fatwire.mda.DimensionableAssetManager;
29 import com.fatwire.system.Session;
30 import com.fatwire.system.SessionFactory;
31 import com.openmarket.xcelerate.asset.AssetIdImpl;
32 import org.apache.commons.lang3.StringUtils;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import tools.gsf.facade.runtag.asset.AssetLoadByName;
36 import tools.gsf.facade.runtag.render.LogDep;
37 import tools.gsf.facade.sql.IListIterable;
38 import tools.gsf.facade.sql.Row;
39 import tools.gsf.facade.sql.SqlHelper;
40
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.Enumeration;
46 import java.util.List;
47 import java.util.Locale;
48
49 public class DefaultLocaleService implements LocaleService {
50 private static final Logger LOG = LoggerFactory.getLogger("tools.gsf.facade.mda.DefaultLocaleService");
51
52
53
54
55
56 private final String localeVar;
57
58
59
60
61
62 private final String langVar;
63
64 private final ICS ics;
65
66 private Session session;
67
68
69
70
71
72
73
74 public DefaultLocaleService(final ICS ics) {
75 this(ics, "lang", "locale");
76 }
77
78
79
80
81
82
83
84
85
86
87 public DefaultLocaleService(ICS ics, String langVar, String localeVar) {
88 if (ics == null) {
89 throw new IllegalArgumentException("ICS must not be null.");
90 }
91
92 this.ics = ics;
93 this.langVar = langVar;
94 this.localeVar = localeVar;
95
96 }
97
98
99
100
101
102
103
104
105 @Override
106 public AssetId findTranslation(final String c, final String cid, final String site,
107 final String preferredLocaleDimensionId) {
108 return findTranslation(new AssetIdImpl(c, Long.parseLong(cid)), site,
109 Long.parseLong(preferredLocaleDimensionId));
110 }
111
112
113
114
115
116
117
118
119
120 @Override
121 public AssetId findTranslation(final AssetId id, final String site, final long preferredDimension) {
122
123 final long dimensionSetId = locateDimensionSetForSite(site);
124
125 return findTranslation(id, preferredDimension, dimensionSetId);
126 }
127
128
129
130
131
132
133
134
135
136 @Override
137 public AssetId findTranslation(final AssetId id, final long preferredDimension, final long dimensionSetId) {
138 if (id == null) {
139 throw new IllegalArgumentException("Required Asset ID missing");
140 }
141
142 if (_isInputAssetDimensionPreferred(id, preferredDimension)) {
143 LOG.debug("Input dimension is already in the preferred dimension. Not invoking dimension set filter. Asset: "
144 + id + ", dimension: " + preferredDimension);
145 return id;
146 } else {
147 LOG.debug("About to look for translations. Input asset id: " + id + ", dimension set: " + dimensionSetId
148 + ", preferred dimension: " + preferredDimension);
149 }
150
151
152
153
154 final DimensionSetInstance dimset = _getDimensionSet(dimensionSetId);
155 return findTranslation(id, preferredDimension, dimset);
156
157 }
158
159
160
161
162
163
164
165
166
167 @Override
168 public AssetId findTranslation(final AssetId id, final long preferredDimension, final String dimensionSetName) {
169 if (id == null) {
170 throw new IllegalArgumentException("Required Asset ID missing");
171 }
172 final Dimension locale = getLocaleForAsset(id);
173
174 if (locale == null) {
175 LOG.debug("Asset is not localized. Not invoking dimension set filter. Asset: " + id);
176 return id;
177 }
178 if (locale.getId().getId() == preferredDimension) {
179 LOG.debug("Input dimension is already in the preferred dimension. Not invoking dimension set filter. Asset: "
180 + id + ", dimension: " + preferredDimension);
181 return id;
182 } else {
183 LOG.debug("About to look for translations. Input asset id: " + id + ", dimension set: " + dimensionSetName
184 + ", preferred dimension: " + preferredDimension);
185 }
186
187
188
189
190 final DimensionSetInstance dimset = getDimensionSet(dimensionSetName);
191
192 return findTranslation(id, preferredDimension, dimset);
193 }
194
195
196
197
198
199
200
201
202
203
204 @Override
205 public AssetId findTranslation(final AssetId id, final long preferredDimension, final DimensionSetInstance dimset) {
206
207 final DimensionFilterInstance filter = _getPopulatedDimensionFilter(dimset, preferredDimension);
208 AssetId translated = findTranslation(id, filter);
209 if (translated == null) {
210 LOG.warn("No translation found for asset " + id + " in dimension set " + dimset.getId() + " for dimension "
211 + preferredDimension + ".");
212 }
213 return translated;
214 }
215
216
217
218
219 private boolean _isInputAssetDimensionPreferred(final AssetId id, final long preferredDimension) {
220 final Dimension dim = getLocaleForAsset(id);
221 if (dim == null) {
222 return true;
223 }
224
225 return dim.getId().getId() == preferredDimension;
226 }
227
228 private static final PreparedStmt FIND_DIMSET_FOR_SITE_PREPAREDSTMT = new PreparedStmt(
229 "select ds.id as id from DimensionSet ds, Publication p, AssetPublication ap where p.name = ? and p.id = ap.pubid and ap.assetid = ds.id and ds.status != 'VO' order by ds.updateddate",
230 Arrays.asList("DimensionSet", "AssetPublication", "Publication"));
231
232 static {
233 FIND_DIMSET_FOR_SITE_PREPAREDSTMT.setElement(0, "Publication", "name");
234 }
235
236
237
238
239
240
241
242
243
244 public long locateDimensionSetForSite(final String site) {
245 if (StringUtils.isBlank(site)) {
246 throw new IllegalArgumentException("Required site name missing");
247 }
248 final StatementParam params = FIND_DIMSET_FOR_SITE_PREPAREDSTMT.newParam();
249 params.setString(0, site);
250 final IListIterable list = SqlHelper.select(ics, FIND_DIMSET_FOR_SITE_PREPAREDSTMT, params);
251
252 final int numRows = list.size();
253 if (numRows == 0) {
254 throw new IllegalStateException(
255 "A DimensionSet has not been defined for site '"
256 + site
257 + "'. Cannot determine any translation unless some locales (Dimensions) are enabled for that site. Aborting operation.");
258 }
259 if (numRows > 1) {
260 final StringBuilder msg = new StringBuilder("More than one dimension set found in site " + site
261 + ". Exactly one is expected. Dimension set ids: ");
262 for (final Row row : list) {
263 final String id = row.getString("id");
264 LogDep.logDep(ics, "DimensionSet", id);
265 msg.append(id).append(" ");
266 }
267 throw new IllegalStateException(msg.append(".").toString());
268 }
269
270 final String id = list.iterator().next().getString("id");
271 LogDep.logDep(ics, "DimensionSet", id);
272 return Long.parseLong(id);
273 }
274
275 private DimensionFilterInstance _getPopulatedDimensionFilter(final DimensionSetInstance dimset,
276 final long dimensionId) {
277
278
279
280
281
282 final Dimension preferredDimension = getDM().loadDimension(dimensionId);
283 if (preferredDimension == null) {
284 throw new RuntimeException("Attempted to load Dimension with id " + dimensionId + " but it came back null");
285 }
286 return _getPopulatedDimensionFilter(dimset, preferredDimension);
287 }
288
289 private DimensionFilterInstance _getPopulatedDimensionFilter(final DimensionSetInstance dimset,
290 final Dimension preferredDimension) {
291 DimensionFilterInstance filter;
292 try {
293 filter = dimset.getFilter();
294 } catch (final DimensionException e) {
295 throw new RuntimeException("Could not get Dimension Filter from DimensionSet", e);
296 }
297
298 filter.setDimensonPreference(Collections.singletonList(preferredDimension));
299 return filter;
300 }
301
302 private DimensionSetInstance _getDimensionSet(final long dimSetId) {
303 final String DIMSET_OBJ_NAME = "LocaleUtils:findTranslation:theDimensionSet:DimensionSet";
304
305 ics.SetObj(DIMSET_OBJ_NAME, null);
306 final FTValList args = new FTValList();
307 args.put("NAME", DIMSET_OBJ_NAME);
308 args.put("TYPE", "DimensionSet");
309 args.put("OBJECTID", Long.toString(dimSetId));
310 args.put("EDITABLE", "FALSE");
311 ics.runTag("ASSET.LOAD", args);
312
313 if (ics.GetErrno() < 0) {
314 throw new IllegalStateException("Could not load dimension set. Errno: " + ics.GetErrno());
315 }
316
317 final Object o = ics.GetObj(DIMSET_OBJ_NAME);
318 ics.SetObj(DIMSET_OBJ_NAME, null);
319 if (o == null) {
320 throw new IllegalStateException("Could not load dimension set but we got no errno... unexpected...");
321 }
322
323 DimensionSetInstance dimset;
324 if (o instanceof DimensionSetInstance) {
325 dimset = (DimensionSetInstance) o;
326 } else {
327 throw new IllegalStateException("Loaded an asset that is not a DimensionSetInstance.");
328 }
329 return dimset;
330 }
331
332
333
334
335
336
337
338
339
340 @Override
341 public DimensionSetInstance getDimensionSet(final String name) {
342 final String DIMSET_OBJ_NAME = "LocaleUtils:findTranslation:theDimensionSet:DimensionSet";
343 ics.SetObj(DIMSET_OBJ_NAME, null);
344 final AssetLoadByName a = new AssetLoadByName();
345 a.setAssetType("DimensionSet");
346 a.setAssetName(name);
347 a.setEditable(false);
348 a.setName(DIMSET_OBJ_NAME);
349 a.execute(ics);
350
351 if (ics.GetErrno() < 0) {
352 throw new IllegalStateException("Could not load dimension set. Errno: " + ics.GetErrno());
353 }
354
355 final Object o = ics.GetObj(DIMSET_OBJ_NAME);
356 ics.SetObj(DIMSET_OBJ_NAME, null);
357 if (o == null) {
358 throw new IllegalStateException("Could not load dimension set but we got no errno... unexpected...");
359 }
360
361 DimensionSetInstance dimset;
362 if (o instanceof DimensionSetInstance) {
363 dimset = (DimensionSetInstance) o;
364 } else {
365 throw new IllegalStateException("Loaded an asset that is not a DimensionSetInstance");
366 }
367 return dimset;
368 }
369
370 private DimensionableAssetManager dam;
371
372
373
374
375
376
377 public DimensionableAssetManager getDAM() {
378 if (dam == null) {
379 dam = getManager(DimensionableAssetManager.class);
380 }
381 return dam;
382 }
383
384 private DimensionManager dm;
385
386
387
388
389
390
391 protected DimensionManager getDM() {
392 if (dm == null) {
393 dm = getManager(DimensionManager.class);
394 }
395 return dm;
396 }
397
398
399
400
401
402
403
404
405
406 @Override
407 public Dimension getLocaleForAsset(final AssetId id) {
408 final Collection<Dimension> dims = getDAM().getDimensionsForAsset(id);
409 for (final Dimension dim : dims) {
410 if ("locale".equalsIgnoreCase(dim.getGroup())) {
411 return dim;
412 }
413 }
414 return null;
415 }
416
417
418
419
420
421
422
423
424
425 @Override
426 public long getDimensionIdForName(final String name) {
427 final AssetId id = getDimensionAssetIdForName(name);
428 return id == null ? -1 : id.getId();
429 }
430
431
432
433
434
435
436
437
438 @Override
439 public AssetId getDimensionAssetIdForName(final String name) {
440 final Dimension dim = getDimensionForName(name);
441 return dim == null ? null : dim.getId();
442 }
443
444
445
446
447
448
449
450
451
452 @Override
453 public Dimension getDimensionForName(final String name) {
454 return getDM().loadDimension(name);
455
456 }
457
458
459
460
461
462
463
464
465
466 @Override
467 public String getNameForDimensionId(final long dimensionid) {
468 final Dimension dim = getDM().loadDimension(dimensionid);
469 return dim == null ? null : dim.getName();
470 }
471
472 protected Session getSession() {
473 if (session == null) {
474 session = SessionFactory.getSession(ics);
475
476 }
477 return session;
478 }
479
480 @SuppressWarnings("unchecked")
481 protected <T> T getManager(final Class<T> c) {
482 return (T) getSession().getManager(c.getName());
483 }
484
485 @Override
486 public AssetId findTranslation(AssetId id, DimensionFilterInstance filter) {
487
488 final Collection<AssetId> relatives = getDAM().getRelatives(id, filter, "Locale");
489
490
491
492 if (relatives == null) {
493 LOG.debug("No translation found for asset " + id + ".");
494 return null;
495 } else {
496 switch (relatives.size()) {
497 case 0: {
498 LOG.debug("No translation found for " + id + ".");
499
500
501
502
503
504
505
506 return null;
507 }
508 case 1: {
509 final AssetId relative = relatives.iterator().next();
510 LOG.trace("LocaleUtils.findTranslation: RELATIVE FOUND... " + relative.getType() + " '"
511 + relative.getId() + "' // errno = " + ics.GetErrno());
512 return relative;
513
514 }
515 default: {
516 throw new IllegalStateException("found more than one translation for asset " + id
517 + " and that is not supposed to be possible.");
518 }
519 }
520 }
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554 public DimensionFilterInstance getDimensionFilter(String site) {
555
556 DimensionFilterInstance filter;
557 try {
558 DimensionSetInstance dimSet = locateDimensionSetInstanceForSite(site);
559 if (dimSet == null) {
560 if (LOG.isTraceEnabled()) {
561 LOG.trace("no DimensionSet returned from getDimensionSet().");
562 }
563 return null;
564 }
565 Collection<AssetId> preferredLocales = getPreferredLocales();
566
567 filter = DimensionUtils.getDimensionFilter(DimensionUtils.getDM(ics), preferredLocales, dimSet);
568 if (LOG.isDebugEnabled()) {
569 LOG.debug("Located dimension filter: " + filter + " in dimensionSet " + dimSet
570 + " with preferred locales: " + preferredLocales + " ");
571 }
572 } catch (DimensionException e) {
573 LOG.error("Could not locate dimension filter", e);
574 filter = null;
575 } catch (RuntimeException e) {
576 LOG.error("Could not locate dimension filter", e);
577 filter = null;
578 }
579 return filter;
580 }
581
582
583
584
585
586
587
588
589 protected final AssetId getExplicitlySpecifiedLocale() {
590
591 String localeName = ics.GetVar(langVar);
592
593 if (StringUtils.isNotBlank(localeName)) {
594 Dimension d = DimensionUtils.getDimensionForName(ics, localeName);
595 if (d != null) {
596 LOG.trace("Preferred locale explicitly set to " + localeName);
597 return d.getId();
598 }
599 }
600 return null;
601 }
602
603
604
605
606
607
608
609 protected final Collection<AssetId> getPreferredLocales() {
610 AssetId result = getExplicitlySpecifiedLocale();
611 if (result != null) {
612 return Collections.singleton(result);
613 }
614
615
616 String l = ics.GetVar(localeVar);
617 try {
618 long localeIdFromVar = Long.parseLong(l);
619 DimensionManager dm = DimensionUtils.getDM(ics);
620 Dimension d = dm.loadDimension(localeIdFromVar);
621 if (d != null) {
622 LOG.trace("Preferred locale detected in ICS context using 'locale' variable: " + localeIdFromVar);
623 return Collections.singletonList(d.getId());
624 }
625 } catch (NumberFormatException e) {
626
627 try {
628 Dimension d = DimensionUtils.getDimensionForName(ics, localeVar);
629 if (d != null) {
630 LOG.trace("Preferred locale detected in ICS context using 'locale' variable: " + localeVar);
631 return Collections.singletonList(d.getId());
632 }
633 } catch (Exception ex) {
634
635 }
636 }
637
638
639
640 String localeSSVar = ics.GetSSVar(localeVar);
641 try {
642 long localeIdFromSSVar = Long.parseLong(localeSSVar);
643 DimensionManager dm = DimensionUtils.getDM(ics);
644 Dimension d = dm.loadDimension(localeIdFromSSVar);
645 if (d != null) {
646 LOG.trace("Preferred locale detected in ICS context using 'locale' session variable: "
647 + localeIdFromSSVar);
648 return Collections.singletonList(d.getId());
649 }
650 } catch (NumberFormatException e) {
651
652 try {
653 Dimension d = DimensionUtils.getDimensionForName(ics, localeSSVar);
654 if (d != null) {
655 LOG.trace("Preferred locale detected in ICS context using 'locale' session variable: "
656 + localeSSVar);
657 return Collections.singletonList(d.getId());
658 }
659 } catch (Exception ex) {
660
661 }
662 }
663
664
665
666 List<AssetId> preferredLocales = new ArrayList<AssetId>();
667 @SuppressWarnings({"rawtypes", "deprecation"})
668 Enumeration locales = ics.getIServlet().getServletRequest().getLocales();
669 while (locales.hasMoreElements()) {
670 Locale locale = (Locale) locales.nextElement();
671 if (locale != null) {
672 String localeName = locale.toString();
673 if (localeName != null && localeName.length() > 0) {
674 try {
675 Dimension dimension = DimensionUtils.getDimensionForName(ics, localeName);
676 if (dimension != null) {
677 preferredLocales.add(dimension.getId());
678 LOG.trace("Found registered locale in user's Accept-Language header (or default): "
679 + localeName);
680 }
681 } catch (RuntimeException e) {
682
683
684
685 LOG.trace(
686 "Found a locale in the user's Accept-Language header, but it was not registered as a dimension: "
687 + localeName + " (this is not usually an error)", e);
688 }
689 }
690 }
691 }
692 return preferredLocales;
693 }
694
695
696
697
698
699
700
701
702 public final DimensionSetInstance locateDimensionSetInstanceForSite(String site) {
703 try {
704 if (StringUtils.isNotBlank(site)) {
705 long discoveredId = locateDimensionSetForSite(site);
706 LOG.trace("Auto-discovered dimension set because there is only one in site " + site + ": DimensionSet:"
707 + discoveredId);
708 return _getDimensionSet(discoveredId);
709 }
710 } catch (RuntimeException e) {
711 if (LOG.isTraceEnabled()) {
712 LOG.trace("Could not auto-discover dimensionset: " + e);
713 }
714 }
715 return null;
716 }
717 }