/*
* Copyright (C) 2014-2015, VistaTEC or third-party contributors as indicated
* by the @author tags or express copyright attribution statements applied by
* the authors. All third-party contributions are distributed under license by
* VistaTEC.
*
* This file is part of Ocelot.
*
* Ocelot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Ocelot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, write to:
*
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301
* USA
*
* Also, see the full LGPL text here: <http://www.gnu.org/copyleft/lesser.html>
*/
package com.vistatec.ocelot.segment.model.okapi;
import java.util.ArrayList;
import java.util.List;
import net.sf.okapi.lib.xliff2.core.Fragment;
import net.sf.okapi.lib.xliff2.core.PCont;
import net.sf.okapi.lib.xliff2.core.Segment;
import net.sf.okapi.lib.xliff2.core.Tag;
import net.sf.okapi.lib.xliff2.core.Tags;
import net.sf.okapi.lib.xliff2.renderer.IFragmentObject;
import net.sf.okapi.lib.xliff2.renderer.XLIFFFragmentRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vistatec.ocelot.segment.model.BaseSegmentVariant;
import com.vistatec.ocelot.segment.model.CodeAtom;
import com.vistatec.ocelot.segment.model.HighlightData;
import com.vistatec.ocelot.segment.model.PositionAtom;
import com.vistatec.ocelot.segment.model.SegmentAtom;
import com.vistatec.ocelot.segment.model.SegmentVariant;
import com.vistatec.ocelot.segment.model.TextAtom;
import com.vistatec.ocelot.segment.model.TextAtom.HighlightBoundaries;
/**
* XLIFF 2.0 segment variant, implemented using the Okapi XLIFF 2.0 library Fragment.
*/
public class FragmentVariant extends BaseSegmentVariant {
private final static Logger LOG = LoggerFactory.getLogger(FragmentVariant.class);
private List<SegmentAtom> segmentAtoms;
private boolean isTarget;
private int protectedContentId = 0;
public FragmentVariant(Segment okapiSegment, boolean isTarget) {
this.isTarget = isTarget;
if (isTarget) {
Fragment frag = okapiSegment.getTarget();
if (frag == null) {
// target elements are optional; make a dummy one if none exists
frag = new Fragment(okapiSegment.getStore(), true);
okapiSegment.setTarget(frag);
}
segmentAtoms = parseSegmentAtoms(frag);
}
else {
segmentAtoms = parseSegmentAtoms(okapiSegment.getSource());
}
}
public FragmentVariant(List<SegmentAtom> atoms, boolean isTarget) {
this.segmentAtoms = atoms;
this.isTarget = isTarget;
}
public FragmentVariant(Fragment frag, boolean isTarget){
this.isTarget = isTarget;
this.segmentAtoms = parseSegmentAtoms(frag);
}
private List<SegmentAtom> parseSegmentAtoms(Fragment frag) {
List<SegmentAtom> parsedSegmentAtoms = new ArrayList<SegmentAtom>();
XLIFFFragmentRenderer fragmentRenderer = new XLIFFFragmentRenderer(frag, null);
for (IFragmentObject fragPart : fragmentRenderer) {
Object textObject = fragPart.getObject();
if (textObject instanceof String) {
TextAtom txtAtom = new TextAtom(fragPart.render());
parsedSegmentAtoms.add(txtAtom);
} else if (textObject instanceof Tag) {
Tag tag = (Tag) textObject;
parsedSegmentAtoms.add(convertToCodeAtom(fragPart, tag));
} else if (textObject instanceof PCont) {
//TODO: Verify usage
parsedSegmentAtoms.add(convertToCodeAtom(fragPart));
} else {
// TODO: More descriptive error
LOG.error("Unrecognized object type in Fragment");
System.exit(1);
}
}
if (highlightDataList != null ){
setAtomsHighlightedText();
}
return parsedSegmentAtoms;
}
private CodeAtom convertToCodeAtom(IFragmentObject fragPart, Tag tag) {
String detailedTag = fragPart.render();
String basicTag = getBasicTag(detailedTag, tag.getId());
return new TaggedCodeAtom(tag, basicTag, detailedTag);
}
private CodeAtom convertToCodeAtom(IFragmentObject fragPart) {
String detailedTag = fragPart.render();
String id = getFragmentObjectId(fragPart);
String basicTag = getBasicTag(detailedTag, id);
return new CodeAtom("PC"+protectedContentId++, basicTag, detailedTag);
}
private String getFragmentObjectId(IFragmentObject fragPart) {
try {
return fragPart.getCTag().getId();
} catch (ClassCastException ex) {
}
try {
return fragPart.getMTag().getId();
} catch (ClassCastException ex) {
}
return "";
}
private String getBasicTag(String detailedTag, String id) {
int tagEndCaratPos = detailedTag.indexOf(">");
if (tagEndCaratPos < 0) {
// TODO: Handle this case
LOG.warn("Could not find tag end character '>' in '"+detailedTag+"'");
System.exit(1);
}
if (detailedTag.charAt(tagEndCaratPos-1) == '/') {
tagEndCaratPos--;
}
int beginTagAttrPos = detailedTag.indexOf(" ");
return "<"+detailedTag.substring(1, beginTagAttrPos >= 0 ? beginTagAttrPos : tagEndCaratPos)
+ id + detailedTag.substring(tagEndCaratPos, detailedTag.length());
}
public Fragment getUpdatedOkapiFragment(Fragment fragment) {
Fragment updatedFragment = new Fragment(fragment.getStore(), fragment.isTarget(), "");
Tags tags = fragment.getTags();
for (SegmentAtom atom : segmentAtoms) {
if (atom instanceof TaggedCodeAtom) {
TaggedCodeAtom codeAtom = (TaggedCodeAtom) atom;
Tag tag = codeAtom.getTag();
if (tag != null) {
updatedFragment.append(Fragment.toChar1(tags.getKey(tag)))
.append(Fragment.toChar2(tags.getKey(tag)));
}
} else if (atom instanceof CodeAtom) {
CodeAtom codeAtom = (CodeAtom) atom;
Tag tag = fetchTag(codeAtom.getId(), fragment);
if (tag != null) {
updatedFragment.append(Fragment.toChar1(tags.getKey(tag)))
.append(Fragment.toChar2(tags.getKey(tag)));
}
} else {
updatedFragment.append(atom.getData());
}
}
return updatedFragment;
}
private Tag fetchTag(String tagId, Fragment fragment) {
Tags tags = fragment.getTags();
for (Tag tag : tags) {
if (tagId.equals(tag.getId())) {
return tag;
}
}
return null;
}
public void updateSegmentAtoms(Segment okapiSegment) {
this.segmentAtoms = parseSegmentAtoms(isTarget ?
okapiSegment.getTarget() : okapiSegment.getSource());
}
@Override
public List<SegmentAtom> getAtoms() {
return this.segmentAtoms;
}
@Override
protected void setAtoms(List<SegmentAtom> atoms) {
this.segmentAtoms = atoms;
}
private List<SegmentAtom> copyAtoms() {
List<SegmentAtom> copyAtoms = new ArrayList<SegmentAtom>();
for (SegmentAtom atom : segmentAtoms) {
if (atom instanceof TextAtom) {
copyAtoms.add(new TextAtom(atom.getData()));
} else if (atom instanceof TaggedCodeAtom) {
TaggedCodeAtom taggedCodeAtom = (TaggedCodeAtom) atom;
copyAtoms.add(new TaggedCodeAtom(taggedCodeAtom.getTag(),
taggedCodeAtom.getData(), taggedCodeAtom.getVerboseData()));
} else if (atom instanceof CodeAtom) {
CodeAtom codeAtom = (CodeAtom) atom;
copyAtoms.add(new CodeAtom(codeAtom.getId(),
codeAtom.getData(), codeAtom.getVerboseData()));
} else if (atom instanceof PositionAtom) {
// Don't copy position atoms because no one will have a handle
// on the new atom so it will be useless.
}
}
return copyAtoms;
}
boolean isTarget() {
return isTarget;
}
@Override
public FragmentVariant createEmptyTarget() {
return new FragmentVariant(new ArrayList<SegmentAtom>(), true);
}
@Override
public FragmentVariant createCopy() {
FragmentVariant copyFragment = new FragmentVariant(copyAtoms(), isTarget);
return copyFragment;
}
@Override
public void setContent(SegmentVariant variant) {
FragmentVariant copy = (FragmentVariant) variant;
this.segmentAtoms = copy.copyAtoms();
}
@Override
public void addHighlightData(HighlightData highlightData) {
super.addHighlightData(highlightData);
if (segmentAtoms != null
&& highlightData.getAtomIndex() < segmentAtoms.size()) {
if (segmentAtoms.get(highlightData.getAtomIndex()) instanceof TextAtom) {
TextAtom txtatom = (TextAtom) segmentAtoms.get(highlightData
.getAtomIndex());
txtatom.addHighlightBoundary(new HighlightBoundaries(
highlightData.getHighlightIndices()[0], highlightData
.getHighlightIndices()[1]));
}
}
}
@Override
public void setHighlightDataList(List<HighlightData> highlightDataList) {
super.setHighlightDataList(highlightDataList);
clearAtomsHighlightedText();
setAtomsHighlightedText();
}
public void setAtomsHighlightedText() {
if (segmentAtoms != null && highlightDataList != null) {
HighlightData hd = null;
TextAtom txtAtom = null;
for(int i = 0; i<highlightDataList.size(); i++){
hd = highlightDataList.get(i);
if (hd.getAtomIndex() < segmentAtoms.size()
&& segmentAtoms.get(hd.getAtomIndex()) instanceof TextAtom) {
txtAtom = (TextAtom) segmentAtoms.get(hd.getAtomIndex());
HighlightBoundaries hb = new HighlightBoundaries(hd
.getHighlightIndices()[0], hd
.getHighlightIndices()[1]);
txtAtom.addHighlightBoundary(hb);
if(currentHighlightedIndex == i){
txtAtom.setCurrentHLBoundaryIdx(txtAtom.getHighlightBoundaries().indexOf(hb));
}
}
}
}
}
@Override
public void removeHighlightData(int atomIndex, int startIndex, int endIndex) {
super.removeHighlightData(atomIndex, startIndex, endIndex);
if(segmentAtoms != null && atomIndex < segmentAtoms.size()){
if(segmentAtoms.get(atomIndex) instanceof TextAtom){
TextAtom txtAtom = (TextAtom) segmentAtoms.get(atomIndex);
txtAtom.removeHighlighBoundary(startIndex, endIndex);
}
}
}
private void clearAtomsHighlightedText() {
if (segmentAtoms != null) {
for (SegmentAtom a : segmentAtoms) {
if (a instanceof TextAtom) {
((TextAtom) a).clearHighlights();
}
}
}
}
@Override
public void clearHighlightedText() {
super.clearHighlightedText();
clearAtomsHighlightedText();
}
@Override
public void setCurrentHighlightedIndex(int currentHighlightedIndex) {
super.setCurrentHighlightedIndex(currentHighlightedIndex);
if(segmentAtoms != null){
if(currentHighlightedIndex > -1 ){
HighlightData hd = highlightDataList.get(currentHighlightedIndex);
if(hd.getAtomIndex() < segmentAtoms.size() && segmentAtoms.get(hd.getAtomIndex()) instanceof TextAtom){
TextAtom txtAtom = (TextAtom)segmentAtoms.get(hd.getAtomIndex());
if(txtAtom.getHighlightBoundaries() != null){
HighlightBoundaries hb = null;
for(int i = 0; i<txtAtom.getHighlightBoundaries().size(); i++){
hb = txtAtom.getHighlightBoundaries().get(i);
if(hb.getFirstIndex() == hd.getHighlightIndices()[0] && hb.getLastIndex() == hd.getHighlightIndices()[1]){
txtAtom.setCurrentHLBoundaryIdx(i);
break;
}
}
}
}
} else {
for(SegmentAtom atom: segmentAtoms){
if(atom instanceof TextAtom) {
((TextAtom)atom).setCurrentHLBoundaryIdx(-1);
}
}
}
}
}
@Override
public void replaced(String newString) {
if (highlightDataList != null) {
HighlightData hd = highlightDataList.get(currentHighlightedIndex);
if (segmentAtoms != null && hd.getAtomIndex() < segmentAtoms.size()
&& segmentAtoms.get(hd.getAtomIndex()) instanceof TextAtom) {
TextAtom currAtom = (TextAtom) segmentAtoms.get(hd
.getAtomIndex());
if (currAtom.getHighlightBoundaries() != null) {
HighlightBoundaries currHb = currAtom
.getHighlightBoundaries().get(
currAtom.getCurrentHLBoundaryIdx());
if (currAtom.getCurrentHLBoundaryIdx() < currAtom
.getHighlightBoundaries().size() - 1) {
int currHbIndex = currAtom.getCurrentHLBoundaryIdx() + 1;
int delta = newString.length()
- (currHb.getLastIndex() - currHb
.getFirstIndex());
HighlightBoundaries nextHb = null;
while (currHbIndex < currAtom.getHighlightBoundaries()
.size()) {
nextHb = currAtom.getHighlightBoundaries().get(
currHbIndex++);
nextHb.setFirstIndex(nextHb.getFirstIndex() + delta);
nextHb.setLastIndex(nextHb.getLastIndex() + delta);
}
}
currAtom.getHighlightBoundaries().remove(currHb);
currAtom.setCurrentHLBoundaryIdx(-1);
}
}
}
super.replaced(newString);
}
}